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。

你可能感兴趣的:(Jotai,Atom,Provider,Store)