当前位置: 首页 > news >正文

Jotai:React轻量级原子化状态管理,告别重渲染困扰

简介

Jotai 是一个为 React 提供的原子化状态管理库,采用自下而上的方法来进行状态管理。Jotai 受 Recoil 启发,通过组合原子来构建状态,并且渲染基于原子依赖性进行优化。这解决了 React 上下文的额外重新渲染问题,并消除了对 memoization 技术的需要。

核心特性

  • 原子化状态 – 状态被分解为原子单元,可以独立管理和组合
  • 零配置 – 无需像 Redux 那样的复杂配置和样板代码
  • 类型安全 – 完全支持 TypeScript,提供良好的类型推断
  • 高性能 – 自动优化渲染,避免不必要的组件重渲染
  • 轻量级 – 核心包仅 2.4kB,API 简洁易用
  • 灵活性 – 支持同步和异步状态,易于派生和组合

快速开始

安装

npm install jotai
# 或
yarn add jotai
# 或
pnpm add jotai

基础用法

import { atom, useAtom } from 'jotai'// 创建一个原子状态
const countAtom = atom(0)function Counter() {// 使用原子状态,类似于 useStateconst [count, setCount] = useAtom(countAtom)return (<div className="flex flex-col items-center justify-center min-h-[300px] p-6"><h1 className="text-3xl font-medium mb-6 text-gray-800">Count: <span className="text-4xl font-bold">{count}</span></h1><div className="flex gap-3"><button onClick={() => setCount(count + 1)}className="px-4 py-2 bg-blue-500 text-white font-medium rounded hover:bg-blue-600">Increment</button><button onClick={() => setCount(count - 1)}className="px-4 py-2 bg-red-500 text-white font-medium rounded hover:bg-red-600">Decrement</button></div></div>)
}export default Counter

原子类型

基础原子

基础原子是最简单的状态单元,可以存储任何类型的值。

import { atom } from 'jotai'// 基础类型
const boolAtom = atom(true)
const numberAtom = atom(42)
const stringAtom = atom('hello')// 复杂类型
const objectAtom = atom({ name: 'John', age: 30 })
const arrayAtom = atom(['apple', 'banana', 'orange'])

派生原子

派生原子可以基于其他原子计算出新的状态,类似于 Vue 的计算属性或 MobX 的计算值。

import { atom } from 'jotai'const countAtom = atom(0)// 只读派生原子
const doubleCountAtom = atom((get) => get(countAtom) * 2)// 可读写派生原子
const countryAtom = atom('Japan')
const citiesAtom = atom(['Tokyo', 'Kyoto', 'Osaka'])const derivedAtom = atom((get) => ({country: get(countryAtom),cities: get(citiesAtom),}),(get, set, newValue) => {// 可以同时更新多个原子set(countryAtom, newValue.country)set(citiesAtom, newValue.cities)}
)

使用原子

Jotai 提供了几种使用原子的方式,根据不同的使用场景选择合适的 Hook。

useAtom

最基本的 Hook,类似于 React 的 useState,用于读取和更新原子状态。

import { atom, useAtom } from 'jotai'const textAtom = atom('hello')function TextInput() {const [text, setText] = useAtom(textAtom)return (<input value={text} onChange={(e) => setText(e.target.value)} />)
}

useAtomValue 和 useSetAtom

当组件只需要读取或只需要写入原子状态时,可以使用这两个 Hook 来优化性能。

import { atom, useAtomValue, useSetAtom } from 'jotai'const countAtom = atom(0)// 只读组件
function DisplayCount() {const count = useAtomValue(countAtom)return <div>Count: {count}</div>
}// 只写组件
function Controls() {const setCount = useSetAtom(countAtom)return (<div><button onClick={() => setCount(c => c + 1)}>+1</button><button onClick={() => setCount(c => c - 1)}>-1</button><button onClick={() => setCount(0)}>Reset</button></div>)
}

高级用法

异步原子

Jotai 支持异步原子,可以处理异步数据获取和更新。

import { atom, useAtom } from 'jotai'// 异步读取原子
const userAtom = atom(async () => {const response = await fetch('https://api.example.com/user')return response.json()
})// 异步写入原子
const postAtom = atom(null,async (get, set, newPost) => {const response = await fetch('https://api.example.com/posts', {method: 'POST',body: JSON.stringify(newPost),})const result = await response.json()// 可以更新其他原子set(postsAtom, [...get(postsAtom), result])return result}
)function AsyncComponent() {const [user, setUser] = useAtom(userAtom)const [, createPost] = useAtom(postAtom)// 使用 React Suspense 处理加载状态return (<div><h1>Welcome, {user.name}</h1><button onClick={() => createPost({ title: 'New Post' })}>Create Post</button></div>)
}

持久化

Jotai 提供了 atomWithStorage 工具函数,可以轻松实现状态的持久化。

import { useAtom } from 'jotai'
import { atomWithStorage } from 'jotai/utils'// 自动保存到 localStorage
const darkModeAtom = atomWithStorage('darkMode', false)function ThemeToggle() {const [darkMode, setDarkMode] = useAtom(darkModeAtom)return (<div><h1>Current theme: {darkMode ? 'Dark' : 'Light'}</h1><button onClick={() => setDarkMode(!darkMode)}>Toggle theme</button></div>)
}

原子族 (atomFamily)

原子族用于创建一组相关的原子,每个原子都有自己的状态,但共享相同的行为。

import { useAtom } from 'jotai'
import { atomFamily } from 'jotai/utils'// 创建一个原子族,每个 ID 对应一个原子
const todoAtomFamily = atomFamily((id) => atom({ id, text: '', completed: false }),(a, b) => a === b
)function TodoItem({ id }) {const [todo, setTodo] = useAtom(todoAtomFamily(id))return (<div><inputtype="checkbox"checked={todo.completed}onChange={() => setTodo({ ...todo, completed: !todo.completed })}/><inputvalue={todo.text}onChange={(e) => setTodo({ ...todo, text: e.target.value })}/></div>)
}

实际应用示例

import { atom, useAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';// 创建原子状态
const todosAtom = atomWithStorage('todos', []); // 存储所有待办事项,使用 atomWithStorage 自动持久化到 localStorage
const todoInputAtom = atom(''); // 存储输入框的值// 过滤类型:全部、已完成、未完成
const filterTypeAtom = atomWithStorage('filterType', 'all');// 派生状态 - 计算已完成和未完成的任务数量
const todoStatsAtom = atom((get) => {const todos = get(todosAtom);const total = todos.length;const completed = todos.filter(todo => todo.completed).length;const uncompleted = total - completed;return { total, completed, uncompleted };
});// 派生状态 - 根据过滤类型筛选任务
const filteredTodosAtom = atom((get) => {const todos = get(todosAtom);const filterType = get(filterTypeAtom);switch (filterType) {case 'completed':return todos.filter(todo => todo.completed);case 'active':return todos.filter(todo => !todo.completed);default:return todos;}
});export default function CssDemo() {const [todos, setTodos] = useAtom(todosAtom);const [filteredTodos] = useAtom(filteredTodosAtom);const [todoInput, setTodoInput] = useAtom(todoInputAtom);const [todoStats] = useAtom(todoStatsAtom);const [filterType, setFilterType] = useAtom(filterTypeAtom);// 添加新的待办事项const addTodo = () => {if (todoInput.trim() === '') return;const newTodo = {id: Date.now(),text: todoInput,completed: false};setTodos([...todos, newTodo]);setTodoInput('');};// 切换待办事项的完成状态const toggleTodo = (id) => {setTodos(todos.map(todo =>todo.id === id ? { ...todo, completed: !todo.completed } : todo));};// 删除待办事项const deleteTodo = (id) => {setTodos(todos.filter(todo => todo.id !== id));};// 清除所有已完成的任务const clearCompleted = () => {setTodos(todos.filter(todo => !todo.completed));};// 全部标记为已完成/未完成const markAllAs = (completed) => {setTodos(todos.map(todo => ({ ...todo, completed })));};return (<div className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-sm"><h1 className="text-2xl font-bold text-gray-800 mb-4 text-center">Todo List</h1>{/* 任务统计信息 */}<div className="flex justify-between text-sm text-gray-500 mb-5 bg-gray-50 p-2 rounded"><span className="px-2 py-1 bg-white rounded shadow-sm">总计: {todoStats.total}</span><span className="px-2 py-1 bg-white rounded shadow-sm">已完成: {todoStats.completed}</span><span className="px-2 py-1 bg-white rounded shadow-sm">未完成: {todoStats.uncompleted}</span></div>{/* 添加待办事项表单 */}<div className="flex mb-5"><inputtype="text"value={todoInput}onChange={(e) => setTodoInput(e.target.value)}onKeyPress={(e) => e.key === 'Enter' && addTodo()}placeholder="添加新的待办事项..."className="flex-1 px-4 py-2 border border-gray-300 rounded-l focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"/><buttononClick={addTodo}className="px-4 py-2 bg-blue-500 text-white font-medium rounded-r hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 transition-colors">添加</button></div>{/* 过滤选项 */}<div className="flex justify-center space-x-2 mb-4"><buttononClick={() => setFilterType('all')}className={`px-4 py-1.5 text-sm rounded-full transition-colors ${filterType === 'all' ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}`}>全部</button><buttononClick={() => setFilterType('active')}className={`px-4 py-1.5 text-sm rounded-full transition-colors ${filterType === 'active' ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}`}>未完成</button><buttononClick={() => setFilterType('completed')}className={`px-4 py-1.5 text-sm rounded-full transition-colors ${filterType === 'completed' ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}`}>已完成</button></div>{/* 待办事项列表 */}<ul className="space-y-2 mb-4 max-h-60 overflow-y-auto pr-1">{filteredTodos.length === 0 ? (<li className="text-gray-500 text-center py-6 border border-dashed border-gray-200 rounded-lg bg-gray-50">{todos.length === 0 ? '暂无待办事项' : '没有符合条件的待办事项'}</li>) : (filteredTodos.map(todo => (<li key={todo.id} className="flex items-center justify-between p-3 border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors"><div className="flex items-center flex-1 min-w-0"><inputtype="checkbox"checked={todo.completed}onChange={() => toggleTodo(todo.id)}className="h-5 w-5 text-blue-500 rounded focus:ring-blue-500"/><span className={`ml-3 truncate ${todo.completed ? 'line-through text-gray-400' : 'text-gray-800'}`}>{todo.text}</span></div><buttononClick={() => deleteTodo(todo.id)}className="text-red-500 hover:text-red-700 focus:outline-none ml-2 flex-shrink-0"><svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clipRule="evenodd" /></svg></button></li>)))}</ul>{/* 底部操作栏 */}{todos.length > 0 && (<div className="flex justify-between pt-4 border-t border-gray-200"><buttononClick={() => markAllAs(true)}className="text-sm text-blue-500 hover:text-blue-700 focus:outline-none transition-colors">全部完成</button><buttononClick={() => markAllAs(false)}className="text-sm text-blue-500 hover:text-blue-700 focus:outline-none transition-colors">全部取消</button><buttononClick={clearCompleted}className={`text-sm ${todoStats.completed === 0 ? 'text-gray-400 cursor-not-allowed' : 'text-red-500 hover:text-red-700'} focus:outline-none transition-colors`}disabled={todoStats.completed === 0}>清除已完成</button></div>)}</div>);
}

与其他状态管理库的比较

Jotai vs Redux

  • 复杂度: Jotai 更简单,没有 actions、reducers、middleware 等概念
  • 样板代码: Jotai 几乎没有样板代码,而 Redux 需要大量样板代码
  • 学习曲线: Jotai 的学习曲线更平缓,API 更接近 React 原生 hooks
  • 适用场景: Jotai 适合中小型应用,Redux 适合大型、复杂的应用

Jotai vs Recoil

  • API: Jotai 的 API 更简洁,不需要 key 字符串
  • 大小: Jotai 更小巧 (2.4kB vs Recoil 的 ~20kB)
  • 配置: Jotai 不需要 Provider 包裹(虽然 SSR 时推荐使用)
  • TypeScript: Jotai 对 TypeScript 的支持更好

Jotai vs Zustand

  • 模型: Jotai 是原子模型,Zustand 是单一 store 模型
  • 集成: Jotai 与 React 集成更紧密,Zustand 可以在 React 外使用
  • 选择: 如果喜欢原子化状态,选 Jotai;如果喜欢单一 store,选 Zustand

最佳实践

  1. 原子粒度: 保持原子粒度适中,既不要过大也不要过小
  2. 原子组织: 将相关原子放在同一个文件中,便于管理
  3. 派生优先: 尽量使用派生原子而不是手动同步状态
  4. Hook 选择: 根据需要选择合适的 Hook (useAtom/useAtomValue/useSetAtom)
  5. 异步处理: 对于异步操作,使用 React Suspense 和 ErrorBoundary

总结

Jotai 是一个轻量级、高性能的 React 状态管理库,采用原子化的方式管理状态。它简化了全局状态管理,提供了优秀的开发体验和运行时性能。特别适合:

  • 中小型 React 应用
  • 需要简单状态管理的项目
  • 对性能有要求的应用
  • 喜欢函数式和原子化思想的开发者

通过原子化的状态管理方式,Jotai 既保持了使用的简单性,又提供了强大的状态组合能力,是 React 应用状态管理的绝佳选择。

 Jotai:React轻量级原子化状态管理,告别重渲染困扰 - 高质量源码分享平台-免费下载各类网站源码与模板及前沿技术分享

http://www.dtcms.com/a/310877.html

相关文章:

  • 《深潜React列表渲染:调和算法与虚拟DOM Diff的优化深解》
  • 《React+TypeScript实战:前端状态管理的安全架构与性能优化深解》
  • Oracle 11g RAC集群部署手册(三)
  • SQL 四大语言分类详解:DDL、DML、DCL、DQL
  • Oracle 11g RAC集群部署手册(一)
  • 探索:Uniapp 安卓热更新
  • flink写paimon表的过程解析
  • cmd怎么取消关机命令
  • 【DL学习笔记】yaml、json、随机种子、浮点精度、amp
  • hcip---ospf知识点总结及实验配置
  • 学习嵌入式第十八天
  • rag学习-以项目为基础快速启动掌握rag
  • 深入 Go 底层原理(十):defer 的实现与性能开销
  • Vue3+ts自定义指令
  • 深入 Go 底层原理(二):Channel 的实现剖析
  • 基于结构熵权-云模型的铸铁浴缸生产工艺安全评价
  • 打靶日记-RCE-labs(续)
  • linux eval命令的使用方法介绍
  • php完整处理word中表单数据的方法
  • 【软考中级网络工程师】知识点之级联
  • PHP面向对象编程与数据库操作完全指南-上
  • ctfshow_源码压缩包泄露
  • Arduino IDE离线安装ESP8266板管理工具
  • 网络安全基础知识【6】
  • Linux初步认识与指令与权限
  • 机器学习sklearn:聚类
  • 读书:李光耀回忆录-我一生的挑战-新加坡双语之路
  • 【物联网】基于树莓派的物联网开发【21】——MQTT获取树莓派传感器数据广播实战
  • Python So Easy 大虫小呓三部曲 - 高阶篇
  • html5+css3+canvas长文转长图工具支持换行