Jotai 是一款轻量级的状态管理工具,大小只有2kb,支持 Typescript。
首先看一下atom的创建函数
// primitive atom
function atom<Value>(initialValue: Value): PrimitiveAtom<Value>
// read-only atom
function atom<Value>(read: (get: Getter) => Value | Promise<Value>): Atom<Value>
// writable derived atom
function atom<Value, Update>(
read: (get: Getter) => Value | Promise<Value>,
write: (get: Getter, set: Setter, update: Update) => void | Promise<void>
): WritableAtom<Value, Update>
// write-only derived atom
function atom<Value, Update>(
read: Value,
write: (get: Getter, set: Setter, update: Update) => void | Promise<void>
): WritableAtom<Value, Update>
如上代码atom分为:原始atom、衍生atom、只读atom、只写atom,以及可读可写atom。
const primitiveAtom = atom(initialValue)
const derivedAtomWithRead = atom(read)
const derivedAtomWithReadWrite = atom(read, write)
const derivedAtomWithWriteOnly = atom(null, write)
下面创建了原始atom countAtom
,衍生atom deriveAtom
,当countAtom中的值发生变化时deriveAtom的值会自动更新。
import { atom } from 'jotai'
// 原始atom
const countAtom = atom(10)
// 衍生atom,依赖另一个atom
const deriveAtom = atom((get) => get(countAtom) * 2);
// 只读
const readOnlyAtom = atom((get) => get(countAtom) * 2);
// 只写
const writeOnlyAtom = atom(null, (get, set, update) => set(countAtom, get(countAtom) * update));
// 可读可写
const readWriteAtom = atom(
(get) => get(countAtom) * 2,
(get, set, update) => {
set(countAtom, update / 2)
}
)
useAtom用于读取atom状态中的值,atom的状态数据保存在store的WeakMap中。像React的useState一样,useAtom以元组的方式返回值和更新函数。
atom最初是没有值的,当atom第一次被useAtom使用时,初始值才会存储在state中。如果atom是衍生的,read函数将会被调用以此来计算初始值。当atom不再被使用时,意味着所有使用它的组件都被卸载了,状态中值将被垃圾回收。
const [value, setValue] = useAtom(countAtom)
如果只是想获取atom的值,不需要修改,可以使用 useAtomValue
:
const count = useAtomValue(countAtom)
同理,如果只想更新atom,不需要获取值,使用 useSetAtom
:
const setCount = useSetAtom(countAtom)
在store中存储atom的状态,store可以作为value传入Provider
,store提供如下三个方法:
useAtomValue
的源码中就通过该方法来触发组件的更新。const myStore = createStore()
const Root = () => (
<Provider store={myStore}>
<App />
</Provider>
)
如果没有提供Provier
,就会使用默认的store,称为Provider-less模式。
默认store创建函数:
const defaultStore = getDefaultStore()
以useAtomValue
、useSetAtom
举例,其中都会使用useStore
,useStore如果没有传入store并且StoreContext中也没store的话就使用默认store。源码如下:
const StoreContext = createContext<Store | undefined>(undefined)
export const useStore = (options?: Options): Store => {
const store = useContext(StoreContext)
return options?.store || store || getDefaultStore()
}
没使用Provider
组件时,StoreContext的值为undefined,这时就调用getDefaultStore
获取默认store。
Provider组件使用React Context为组件的子树提供状态,多个Provider可以为多个子树提供状态,并且可以嵌套使用。
如果atom没有在provider下使用,就使用默认store,称之为Provider-less模式。
Provider的作用有一下三点:
const SubTree = () => (
<Provider>
<Child />
</Provider>
)
Provider的类型签名
const Provider: React.FC<{
store?: Store
}>
atom的值并不在atom自身,而是保存在对应的store中,Provider是一个包含store并为子组件的atom提供值的组件。如果需要给不同的子树提供不同的atom值设置Provider是很有必要的。
举例:
// app.tsx
const myStore = createStore()
const Root = () => (
<Provider store={myStore}>
<App />
</Provider>
)
// component.tsx
const Component = () => {
const store = useStore()
const [count, setCount] = useAtom(countAtom, {store})
// ...
}
useStore的第二个参数可以指定从哪个store中获取atom的值,如果不传就从StoreContext中取,StoreContext中没有就使用默认的store。