type
status
date
slug
summary
tags
category
icon
password

Jotai

Jotai是一个原始且灵活的React状态管理库。
  • 原始:提供了Hooks方式的Api,使用方式类似于useStateuseReducer
  • 灵活:可以组合多个 Atom 来创建新的 Atom,并且支持异步
  • 高性能:更新对应的 Atom 只会重新渲染订阅了这个 Atom 的组件,并不会像 Context 那样导致整个父组件重新渲染,所以可以做到精确渲染

定义 Atom

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

使用 Atom

useAtom函数接受一个参数,参数值为 一个Atom
返回值是一个数组
数组第一个值是 Atom 存储的值,第二个值是更新 Atom 值的函数

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

定义或者派生使用时,atom()需要放在函数外,否则会造成死循环。
建议维护一份ts文件单独存放定义的原子

1.Read-only atom

2.Write-only atom

3.Read-Write atom

Provider(opens new window)

Provider 是为一个组件子树提供状态。多个 Provider 可以用于多个子树,甚至可以嵌套。这就像 React Context 一样工作。
在不提供Provider的情况下,会使用默认状态,我们可以不提供他来包裹使用Atom的组件。
使用 Provider 带来的效果
  1. 为每个组件树提供不同的状态
  1. 包含了一些调试信息
  1. 接受原子的初始值initialValues

onMount

创建后的原子可以有一个可选的属性onMountonMount是一个函数,它接受一个setAtom函数,并可以返回onUnmount函数:一个卸载的函数(类似于useEffect的使用方法)。
onMount当原子首次在提供程序中使用时调用该函数,不再使用时会触发onUnmount卸载函数。

Utils

内置了很多工具方法 (opens new window),可以根据需要选择使用。更多方法详见上方文档
1.持久化存储的工具方法
2.原子拆解选择器

第三方集成

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

调试工具

Jotai官方提供了两种Debug方式:
  1. 用Redux DevTools去查看特定atom的状态,只需要将atom和label传进jotai/devtools中的useAtomDevtools Hooks。
  1. 用React Dev Tools去查看Jotai的Provider,全部atom的状态都存放在DebugState中,但需要额外设定atom的debugLabel作为key,否则会显示为<no debugLabel>

Mobx

mobx

一个基于 react 的一个响应式更新状态库,比较好用的一点是,其定义的数据可以类似 state 被 UI 所追踪,用 vue 的类似实现来写 react,视图与逻辑分离,还是挺爽的。

示例

基本概念

  1. State(状态)
  1. Actions(动作)
  1. Derivations(派生)

State

数据状态。
如何将数据标识为 mobx 的state?
通过 makeObservable 并将属性标识为 observable:

Actions

更改数据状态。
可以理解为数据提交,主要是为了防止直接修改数据,使得数据的更改变得难以追踪和维护。
如上面示例中的 toggle 就是一个 action,它通知 finished 变更为 !finished。

Derivations

派生:任何基于 state 进行处理的东西。

主要分为两大类:

1.Computed :计算属性,基于 state 二次加工的数据。
2.Reactions:变化,当 State 改变时需要自动运行的副作用。
示例:
这里将 unfinishedTodoCount 标识为 computed,每当其依赖的状态(即todos)变更时,它也会跟着变更,底层原理:proxy。
Reaction 其实原理与其类似,只不过为了分类将其概念抽离了出来,其不返回数据,只产生副作用,一般用来做什么?如打印到控制台、发出网络请求、增量更新 React 组件树以便更新 DOM 等。
到目前为止,最常用的 reaction 形式是UI组件。

如何自定义 reaction :

通常情况下你不需要使用它们,可以使用 autorun (opens new window)reaction (opens new window)when (opens new window)方法来订制你的特殊业务场景。
比如,下面的 autorun 将在unfinishedTodoCount的数量发生变化时输出日志。

响应式 React 组件

使用 observer 将 react 组件与 state 相关联。

observable state

一共有三种方式创建可观测状态。
1.makeObservable。捕获监听对象并手动映射注解。
2.makeAutoObservable。加强版的 makeObservable ,会自动映射注解(减低心智,推荐使用)。
3.observable。以函数调用的方式进行注解,将整个对象变为可观测状态。

makeObservable

  • makeObservable(target, annotations?, options?)

makeAutoObservable

  • makeAutoObservable(target, overrides?, options?)
自动推断规则:
  • 所有 自有 属性都成为 observable
  • 所有 getters 都成为 computed
  • 所有 setters 都成为 action
  • 所有 prototype 中的 functions 都成为 autoAction
  • 所有 prototype 中的 generator functions 都成为 flow。(需要注意,generators 函数在某些编译器配置中无法被检测到,如果 flow 没有正常运行,请务必明确地指定 flow 注解。)
  • overrides 参数中标记为 false 的成员将不会被添加注解。例如,将其用于像标识符这样的只读字段。

observable

  • observable(source, overrides?, options?)
使用 proxy 包装对象,并将其变为可观测的。
特性:
  • 之后被添加到这个对象中的属性也将被侦测并使其转化为可观察对象(除非禁用 proxy (opens new window)

可用的注解(opens new window)

注解
描述
observable observable.deep
定义一个存储 state 的可跟踪字段。如果可能,任何被赋值给 observable 的字段都会基于它自己的类型被(深度)转化为observableautoActionflow。只有 plain objectarrayMapSetfunctiongenerator function 可以转换,类实例和其他实例不会被影响。
observable.ref
类似于 observable,但只有重新赋值才会被追踪。所赋的值会被完全忽略,并且将不会主动转化为 observable/autoAction/flow。比方说,在你打算将不可变数据存储在可观察字段中时,可以使用这个注解。
observable.shallow
类似于 observable.ref 但是是用于集合的。任何所赋的集合都会被转化为可观察值,但是其内部的值并不会变为可观察值。
observable.struct
类似于 observable,但是会忽略所赋的值中所有在结构上与当前值相同的值。
action
把一个函数标记为会修改 state 的 action。查看 actions (opens new window)获取更多信息。不可写。
action.bound
类似于 action,但是会将 action 绑定到实例,因此将始终设置 this。不可写。
computed
可以用在 getter (opens new window)上,用来将其声明为可缓存的派生值。查看 computeds (opens new window)获取更多信息。
computed.struct
类似于 computed,但如果重新计算后的结果在结构上与之前的结果相等,那么观察者将不会收到通知。
true
推断最佳注解。查看 makeAutoObservable (opens new window)获取更多信息。
false
刻意不为该属性指定注解。
flow
创建一个 flow 管理异步进程。查看 flow (opens new window)获取更多信息。需要注意的是,推断出来的 TypeScript 返回类型可能会出错。 不可写。
flow.bound
类似于 flow, 但是会将 flow 绑定到实例,因此将始终设置 this。 不可写。
override
autoAction
不应被显式调用,但 makeAutoObservable 内部会对其进行调用,以便根据调用上下文将方法标识为 action 或者派生值。

局限性(opens new window)

详细见文档链接。

Options(opens new window)

上面的 API 都有一个可选的 options 参数,该参数是一个对象,支持以下选项:
  • autoBind: true 默认使用 action.bound/flow.bound,而不使用 action/flow。不影响被显式注释过的成员。
  • deep: false 默认使用 observable.ref,而不使用 observable。不影响被显式注释过的成员。
  • name: <string> 为对象提供一个调试名称,该名称将被打印在错误消息和 reflection API 中。

将 observable 转换回普通的 JavaScript 集合

有时有必要将可观察的数据结构转换回原生的数据结构。 例如,将可观察对象传入一个无法跟踪可观察对象的 React 组件时,或者想要获取一个不会再被更改的副本时。
要进行浅转换,用常用的 JavaScript 操作就可以做到:
要将数据树递归地转换为普通对象,可使用 toJS (opens new window)工具函数。 对于类,建议实现一个 toJSON() 方法,因为这样会被 JSON.stringify 识别出来。

actions

使用 action 注解来修改 state 状态。

使用 action 包装函数

action 不仅仅是一个注解,更是一个高阶函数,因此一切涉及修改 state 的操作,都应该将其标记为 action。action 不会触发 state 的追踪。
示例:

action 绑定

为了解决 this 的指向问题,可以使用 action.bound 使 this 始终正确的绑定在函数内部。
(使用 makeAutoObservable(o, {}, { autoBind: true }) 会自动绑定所有的 actions 和 flows)

临时执行的 action

可以使用 runInAction(fn) 来立即执行一个临时定义的 action。这在异步进程中非常有用。

异步执行 action

任何的 state 的更改操作都应该处于 action 作用域中,因此,当处理 Promise 时,涉及 state 修改的操作都应当标识为action。

async / await 的替代方案

async / await 作为现代 js 中常用的异步方案,在 mobx 中不推荐使用,而是推荐 flow 作为替代。
Why? 因为 async / await 的本质是 promise 的包装,如上面的 fetchProjects,就是把await后的代码放到Promise.then()函数里,把catch部分的代码放到.catch的回调函数里,还是同样需要将异步的执行部分用 action 来包装起来,说白了,其本质还是一个 Promise 任务

使用 flow 进行替代

实现:generator 迭代器

flow.bound

用法与 action.bound 类似,使this 始终被正确绑定在函数内部。

computeds

基于 state 二次处理数据。
标识:computed,或使用 makeAutoObservable 自动将所有的 getters 标识为 computed。

示例

下面的示例依靠 Reactions {🚀} (opens new window)高级部分中的 autorun (opens new window)来辅助说明计算值的意义。
notion image

最佳实践

  1. 它们不应该有副作用或者更新其他可观察对象。
  1. 避免创建和返回新的可观察对象。
  1. 它们不应该依赖非可观察对象的值

Options {🚀}

name

equals

默认设置为 comparer.default。它充当一个比较函数,用于比较上一个值和下一个值。如果该函数认为两个值相等,那么观察者们将不会被重新计算。

内置 comparers

MobX 提供了四种内置的 comparer 方法,这些方法满足 computedequals 选项的大多数需求:
  • comparer.identity 使用全等 (===)运算符确定两个值是否相同。
  • comparer.defaultcomparer.identity 相同,但是其认为 NaN 等于 NaN
  • comparer.structural 执行深层的结构比较以确定两个值是否相同。
  • comparer.shallow 执行浅层的结构比较以确定两个值是否相同。

示例结构比较{👀}

当返回一个新的对象时,即时值一样,观察者们也会默认数据不一样,并会触发监察者。
如果需要进行结构比较可以使用 computed.struct

keepAlive`

使计算属性的值在未被观察时被暂时停用。

reactions

reactions 是 mobx 中相当重要的概念。和 computed 一样都会在 state 依赖变更时触发,但不同的是
reactions 不处理数据,而是基于数据更改而产生的副作用。
类似于 useEffect 勾子。

autorun

依赖变更时执行,创建时也会执行一次。
示例:

reaction

类似于 autorun,但可以指定追踪的依赖。
  • reaction(() => value, (value, previousValue, reaction) => { sideEffect }, options?)
  1. 第一个值:监听的依赖;
  1. 第二个值:一个函数;参数分别为当前依赖值,依赖变更前的值,reaction;
  1. 第三个值:options 选项。

when

判断依赖,在指定依赖达到条件时执行。
  • when(predicate: () => boolean, effect?: () => void, options?)
  • when(predicate: () => boolean, options?): Promise
示例:

异步

如果你没有提供 effect 函数,when 将会返回一个 Promise。这样会跟 async / await 很好地结合在一起,让你可以等待可观察对象中的变化。
如果要提前取消 when,可以对它返回的 Promise 调用 .cancel() 函数。

规则

  1. 默认情况下,如果可观察对象发生了改变,受其影响的 reactions 会立即(同步)运行。然而,它们直到当前最外层的 (trans)action 执行结束后才会运行。
  1. autorun 只会跟踪给定函数在同步执行过程中所读取的可观察对象,不会跟踪异步发生的变化。
  1. autorun 不会跟踪被其调用的 action 所读取的可观察对象,因为 action 始终不会被追踪

优化

由于 reactive 会一直监听并等待依赖变更,所以强烈推荐在不需要执行副作用时将它们注销掉。
reactive 会返回一个函数,执行它,销毁 reactive 。
防止内存泄漏:

options

  • name
该字符串在 Spy event listeners (opens new window)MobX developer tools (opens new window)中用作此 reaction 的调试名称。
  • fireImmediately
布尔值,指示在第一次运行 data 函数后立即触发 effect 函数。默认为 false
  • delay
对 effect 函数进行节流的毫秒数。如果是 0(默认值),将不进行节流操作。
  • timeout
设置一段有限的 when 将要等待的时间。如果过了时限,when 将会 reject 或抛出错误。
  • onError
异常行为处理
  • scheduler(autorun, reaction)
设置一个自定义调度程序,用来决定应该如何调度 autorun 函数的重新运行。它接受一个函数作为参数,这个函数应该在将来 某个时刻被调用,例如:{ scheduler: run => { setTimeout(run, 1000) }}
  • equals
默认情况下设置为 comparer.default。如果指定,则此比较函数用于比较 data 函数产生的上一个值和下一个值。当且仅当 此函数返回 false 时,effect 函数才会被执行。
React笔记-其他React笔记-Redux
Loading...
慕雨
慕雨
一个普通的干饭人🍚
公告
🎉欢迎来到慕雨的博客小站🎉
这里是我的个人学习、生活记录
--- 免责声明 ---
⚠️ 本站内容仅代表个人观点
⚠️ 本站内容仅供学习参考使用