Jotai使用笔记(一)

更新于 阅读 5
Jotai使用笔记(一)

Jotai

Jotai 是一款轻量级的状态管理工具,大小只有2kb,支持 Typescript。

1. Jotai的组成部分

  1. store: 主要用于数据的存储、依赖的收集和更新,已经更新通知
  2. atom:创建原子,或者衍生新的原子
  3. useAtom:将atom与视图关联起来(将atom与store链接起来,同时订阅store中atom得变化)
  4. 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()

useAtomValueuseSetAtom举例,其中都会使用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的作用有一下三点:

  1. 为不同的子树提供不同的状态。
  2. 接受atom的初始值
  3. 通过重新挂载清除所有原子
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。