Jotai是一个原始而灵活的React状态管理库,它的api都是以hook的方式提供,类似于useState、useRecducer等。可以组合多个Atom来创建新的Atom。
Atom是Jotai中状态管理单位,它是可以更新和订阅的,当atom被更新时,订阅了这个atom的组件便会重新渲染。并且,更新对应的Atom只会重新渲染订阅了这个Atom的组件,并不会像Context那样导致整个父组件重新渲染。
Jotai的简单使用示例
import ProTable from '@/components/proTable/ProTable'
import SpaceItem from '@/components/spaceItem/SpaceItem'
import WhiteSpace from '@/components/whiteSpace'
import { getRandomIntInclusive } from '@/utils'
import { Alert, Button, Card, Input, Space, Tag } from 'antd'
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'
import { throttle } from 'lodash'
import { ChangeEvent, useState } from 'react'
const countAtom = atom<number>(0) // 定义一个原子
const isEvenAtom = atom((get) => get(countAtom) % 2 === 0) // 定义一个派生原子
const isOddAtom = atom((get) => !get(isEvenAtom)) // 定义一个派生原子
const nameAtom = atom('Jotai')
const name1Atom = atom('Zutand')
const stateAtom = atom(
(get) =>
`${get(nameAtom)}和${get(name1Atom)}是React生态中比较好用的状态管理库`,
)
type Theme = 'light' | 'dark'
const themeAtom = atom<Theme>('light')
const themeColorAtom = atom(
(get) => (get(themeAtom) === 'light' ? '#ce2345' : '#1fe345'),
(get, set, theme: Theme) => set(themeAtom, theme),
)
type TodoItem = {
id: number
title: string
completed: boolean
}
const todoListAtom = atom<TodoItem[]>([
{
id: 1,
title: '学习Jotai',
completed: false,
},
{
id: 2,
title: '学习React',
completed: false,
},
])
// Jotai状态管理
export default function Jotai() {
const [count, setCount] = useAtom(countAtom)
const [isOdd] = useAtom(isOddAtom)
const isEven = useAtomValue(isEvenAtom)
const setCounts = useSetAtom(countAtom)
const state = useAtomValue(stateAtom)
const [themeColor, setThemeColor] = useAtom(themeColorAtom)
const [theme, setThemes] = useAtom(themeAtom)
const setTheme = useSetAtom(themeAtom)
const countHandler = (type: 'add' | 'sub' | 'random') => () => {
setCount((pre) =>
type === 'add'
? pre + 1
: type === 'sub'
? pre - 1
: getRandomIntInclusive(),
)
}
// 切换主题
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light')
}
const [inputValue, setInputValue] = useState('')
const [todoList, setTodoList] = useAtom(todoListAtom)
const onInputChange = (event: ChangeEvent<HTMLInputElement>) => {
setInputValue(event.target.value)
}
const addItem = () => {
if (inputValue) {
setTodoList([
...todoList,
{
id: Date.now(),
title: inputValue,
completed: false,
},
])
setInputValue('')
}
}
const toggleComplete = (record: TodoItem) => () => {
setTodoList((pre) => {
const matchIndex = pre.findIndex((item) => item.id === record.id)
return Array.from(
pre.fill(
{ ...record, completed: !record.completed },
matchIndex,
matchIndex + 1,
),
)
})
}
const delTodo = (record: TodoItem) => () => {
setTodoList((pre) => {
return pre.filter((item) => item.id !== record.id)
})
}
return (
<Card>
<Alert message="展示Jotai状态管理的用法" type="success" />
<WhiteSpace />
<div>当前的count是:{count}</div>
<div>{`是否是偶数:${isEven ? '是' : '否'}`}</div>
<div>{`是否是奇数:${isOdd ? '是' : '否'}`}</div>
<div>{state}</div>
<WhiteSpace />
<SpaceItem align="left">
<Button onClick={countHandler('add')} type="primary">
加
</Button>
<Button onClick={countHandler('sub')} danger>
减
</Button>
<Button onClick={countHandler('random')}>随机数</Button>
<Button
onClick={() => {
setCounts((pre) => pre + 1)
}}
>
设置加一
</Button>
<Button
onClick={() => {
setCounts(getRandomIntInclusive())
}}
>
设置随机数
</Button>
</SpaceItem>
<WhiteSpace />
<div>当前主题是:{theme}</div>
<div style={{ color: themeColor }}>当前主题色是:{themeColor}</div>
<SpaceItem align="left">
<Button type="primary" onClick={toggleTheme}>
切换主题
</Button>
<Button
type="primary"
onClick={() => {
setThemes((pre) => (pre === 'light' ? 'dark' : 'light'))
}}
>
切换主题2
</Button>
<Button type="primary" onClick={() => setThemeColor('light')}>
切换主题3
</Button>
</SpaceItem>
<WhiteSpace gap={40} />
<Alert message="用Jotai实现一个TodoList" type="success" />
<ProTable
pagination={false}
dataSource={todoList}
rowKey="id"
columns={[
{
title: 'ID',
dataIndex: 'id',
},
{
title: '标题',
dataIndex: 'title',
},
{
title: '是否完成',
dataIndex: 'completed',
render: (completed: boolean) => (
<Tag color={completed ? 'success' : 'error'}>
{completed ? '已完成' : '未完成'}
</Tag>
),
},
{
title: '操作',
key: 'action',
width: 200,
align: 'center',
render: (_, record: TodoItem) => {
const isCompleted = record.completed
return (
<SpaceItem>
<Button
onClick={toggleComplete(record)}
type="primary"
danger={isCompleted}
>
{isCompleted ? '未完成' : '完成'}
</Button>
<Button onClick={delTodo(record)} danger>
删除
</Button>
</SpaceItem>
)
},
},
]}
/>
<WhiteSpace />
<Space.Compact block>
<Input
value={inputValue}
onChange={onInputChange}
allowClear
placeholder="请输入"
style={{ width: 500 }}
/>
<Button
type="primary"
disabled={!inputValue}
onClick={throttle(addItem, 1500)}
>
添加
</Button>
</Space.Compact>
</Card>
)
}
完整效果