Jotai--React状态管理

4,056 阅读4分钟
官网npm周下载量Github starTS特点Gziped size
Jotaijotai.org/88,7567.7k支持小巧、简洁且高性能约3kb

Jotai是一个原始且灵活的React状态管理库。

  • 原始:提供了Hooks方式的Api,使用方式类似于useStateuseReducer

  • 灵活:可以组合多个 Atom 来创建新的 Atom,并且支持异步

  • 高性能:更新对应的 Atom 只会重新渲染订阅了这个 Atom 的组件,并不会像 Context 那样导致整个父组件重新渲染,所以可以做到精确渲染

定义 Atom

atom: 原子的意思。一个Atom代表一个状态,使用atom函数创建一个Atom,需要传入一个参数作为初始值。

import { atom } from 'jotai';

// 定义后并没有与原子关联的值。只有通过 useAtom 使用后的原子,初始值才会存储在状态中

export const textAtom = atom('hello');

// Ts的使用和 React.useState 的方式相同,基础类型可以不定义类型

const numAtom = atom<number>(0)

const numAtom = atom<number | null>(0)

const arrAtom = atom<string[]>([])

使用 Atom

useAtom 函数接受一个参数,参数值为 一个 Atom

返回值是一个数组

数组第一个值是 Atom 存储的值,第二个值是更新 Atom 值的函数

  import { useAtom } from 'jotai';

  // 类似 React.useState 的用法
  // text 读取 textAtom 的值,setText更改值并通知订阅了 textAtom 的组件更新
  const [text, setText] = useAtom(textAtom);

创建派生原子,有三种模式

定义或者派生使用时,atom()需要放在函数外,否则会造成死循环。

建议维护一份ts文件单独存放定义的原子

  1. Read-only atom

// 只读的 atom(Read-only atom)
const textLenAtom = atom((get) => get(textAtom).length);

// 只读,没有第二个参数
const [len] = useAtom(textLenAtom);
  1. Write-only atom

// 只写的 atom
const writeOnlyAtom = atom(
  null, // 约定为第一个参数传递 `null`
  (get, set, update) => {
    // `update` 是我们收到的用于更新这个原子的任何单个值
    set(textAtom, `${get(textAtom)} ${update}`);
  },
);

// 只写的 atom,第一个参数为 null 不可用
const [, setWrite] = useAtom(writeOnlyAtom);
const handleWrite = () => {
    setWrite('world');
};
  1. Read-Write atom

// 3. 可读可写的 atom
const readWriteAtom = atom(
  (get) => `${get(textAtom)}~`,
  (get, set, update: { text: string }) => {
    // `update` 是我们收到的用于更新这个原子的任何单个值
    set(textAtom, `${get(textAtom)} ${update.text}`);
  },
);

const [rw, setRw] = useAtom(readWriteAtom);
const handleReadWriteAtom = () => {
    setRw({ text: 'world' });
    console.log(rw); // hello world~
};

Provider

Provider 是为一个组件子树提供状态。多个 Provider 可以用于多个子树,甚至可以嵌套。这就像 React Context 一样工作。

在不提供Provider的情况下,会使用默认状态,我们可以不提供他来包裹使用Atom的组件。

<Provider
   initialValues={[
     [textAtom, 'initval'], // 给 textAtom 原子提供初始值
   ]}
>
   <Input />
   <CharCount />
   <Uppercase />
</Provider>

使用 Provider 带来的效果
1. 为每个组件树提供不同的状态
2. 包含了一些调试信息
3. 接受原子的初始值initialValues

onMount

创建后的原子可以有一个可选的属性onMountonMount是一个函数,它接受一个setAtom函数,并可以返回onUnmount函数:一个卸载的函数(类似于useEffect的使用方法)。

onMount当原子首次在提供程序中使用时调用该函数,不再使用时会触发onUnmount卸载函数。

// 顺带着使用派生用法模拟下 reducer

const derivedAtom = atom(
  (get) => get(countAtom),
  (get, set, action: { type: 'init' | 'inc' | 'dec' }) => {
      if (action.type === 'init') {
        set(countAtom, 8);
      } else if (action.type === 'inc') {
        set(countAtom, (c) => c + 1);
      } else if (action.type === 'dec') {
        set(countAtom, (c) => c - 1);
      }
  },
);

const [count2, setCount2] = useAtom(derivedAtom);

derivedAtom.onMount = (setAtom) => {
  setAtom({ type: 'init' })
}

Utils

内置了很多工具方法,可以根据需要选择使用。

// 以持久化存储的工具方法举例,更多方法详见上方文档

import { atomWithStorage, RESET } from 'jotai/utils';

// atomWithStorage()第一个参数是storage的key值,第二个参数是默认值

const textAtom = atomWithStorage('AtomWithStorageKey', 'hello');



const [text, setText] = useAtom(textAtom);

// RESET可以清楚该storage

setText(RESET)

第三方集成

Jotai内部对多个状态库进行了继承,可根据需要安装对应的包,来配合Jotai使用。

调试工具

Jotai官方提供了两种Debug方式:

  1. 用Redux DevTools去查看特定atom的状态,只需要将atom和label传进jotai/devtools中的useAtomDevtools Hooks。
  2. 用React Dev Tools去查看Jotai的Provider,全部atom的状态都存放在DebugState中,但需要额外设定atom的debugLabel作为key,否则会显示为<no debugLabel>
// 1. Redux DevTools 调试
import { useAtomDevtools } from 'jotai/devtools'

useAtomDevtools(countAtom, 'aaa');

// React DevTools 调试
countAtom.debugLabel = '第一个count';

使用感受

几个月使用下来感觉还是很不错的,非常方便,很多原本使用useState的现在会用useAtom代替,当然亦不可滥用,过犹不及,最好还是用于那些真正有全局使用场景的需求下,或者组件嵌套层级多,不想一层层传递的情况。 但毕竟不是useState,如果在自己的组件中使用,组件卸载时需要注意恢复默认值。避免再次进入时状态混乱。

所以最好还是合理使用useAtom。