Jotai使用笔记(一)
Jotai
Jotai 是一款轻量级的状态管理工具,大小只有2kb,支持 Typescript。
1. Jotai的组成部分
- store: 主要用于数据的存储、依赖的收集和更新,已经更新通知
- atom:创建原子,或者衍生新的原子
- useAtom:将atom与视图关联起来(将atom与store链接起来,同时订阅store中atom得变化)
- Provider: 传图store来隔离数据,如果不传将使用默认store
2 atom
首先看一下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) } )
2.1 useAtom
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)
3 store
在store中存储atom的状态,store可以作为value传入Provider
,store提供如下三个方法:
- get:获取atom的值
- set:设置atom的值
- sub:订阅atom的更新,当atom值发生改变后会调用订阅的函数,
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
Provider组件使用React Context为组件的子树提供状态,多个Provider可以为多个子树提供状态,并且可以嵌套使用。 如果atom没有在provider下使用,就使用默认store,称之为Provider-less模式。
Provider的作用有一下三点:
- 为不同的子树提供不同的状态。
- 接受atom的初始值
- 通过重新挂载清除所有原子
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。