夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 核心概念

  • 高级指引

  • Hook

  • 案例演示

  • Router

  • Redux

  • Jotai

    • Jotai
    • Mobx

    • 其他

    • 《React》笔记
    • Jotai
    夜猫子
    2022-11-03
    目录

    Jotai

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

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

    • 灵活:可以组合多个 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[]>([])
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 使用 Atom

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

    返回值是一个数组

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

      import { useAtom } from 'jotai';
    
      // 类似 React.useState 的用法
      // text 读取 textAtom 的值,setText更改值并通知订阅了 textAtom 的组件更新
      const [text, setText] = useAtom(textAtom);
    
    1
    2
    3
    4
    5

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

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

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

    # 1.Read-only atom

    // 只读的 atom(Read-only atom)
    const textLenAtom = atom((get) => get(textAtom).length);
    
    // 只读,没有第二个参数
    const [len] = useAtom(textLenAtom);
    
    1
    2
    3
    4
    5

    # 2.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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    # 3.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~
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    # Provider (opens new window)

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

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

    <Provider
       initialValues={[
         [textAtom, 'initval'], // 给 textAtom 原子提供初始值
       ]}
    >
       <Input />
       <CharCount />
       <Uppercase />
    </Provider>
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    使用 Provider 带来的效果

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

    # onMount

    创建后的原子可以有一个可选的属性onMount。onMount是一个函数,它接受一个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' })
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    # Utils

    内置了很多工具方法 (opens new window),可以根据需要选择使用。更多方法详见上方文档

    1.持久化存储的工具方法

    import { atomWithStorage, RESET } from 'jotai/utils';
    
    // atomWithStorage()第一个参数是storage的key值,第二个参数是默认值
    
    const textAtom = atomWithStorage('AtomWithStorageKey', 'hello');
    
    const [text, setText] = useAtom(textAtom);
    
    // RESET可以清除该storage
    
    setText(RESET)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    2.原子拆解选择器

    import deepEquals from "fast-deep-equal";
    
    const defaultPerson = {
      name: {
        first: 'Jane',
        last: 'Doe',
      },
      birth: {
        year: 2000,
        month: 'Jan',
        day: 1,
        time: {
          hour: 1,
          minute: 1,
        },
      },
    }
    
    // 数据源原子
    const personAtom = atom(defaultPerson)
    
    // 进行数据拆解出name
    const nameAtom = selectAtom(personAtom, (person) => person.name)
    
    // 进行数据拆解出birth,第三个参数传入 equalityFn 判断是否一致,有助于避免无效的渲染,提高性能
    const birthAtom = selectAtom(personAtom, (person) => person.birth, deepEquals)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

    # 第三方集成

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

    • Immer (opens new window)

    • Optics (opens new window)

    • Query (opens new window)

    • XState (opens new window)

    • Valtio (opens new window)

    • Zustand (opens new window)

    • Redux (opens new window)

    • URQL (opens new window)

    # 调试工具

    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';
    
    1
    2
    3
    4
    5
    6
    7
    编辑 (opens new window)
    上次更新: 2024/12/19 17:25:54
    Redux Toolkit详细示例
    Mobx

    ← Redux Toolkit详细示例 Mobx→

    最近更新
    01
    IoC 解决了什么痛点问题?
    03-10
    02
    如何调试 Nest 项目
    03-10
    03
    Provider注入对象
    03-10
    更多文章>
    Copyright © 2019-2025 Study | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式