Mobx4 对 Observables 响应
(@)computed
计算值(computed values)是可以根据现有的状态或其它计算值衍生出的值。computed
和 autorun
都是响应式调用的表达式。
如果你想响应式的产生一个可以被其它 observer 使用的值,请使用 @computed;如果你不想产生一个新值,而想要达到一个效果,请使用 autorun。
如果任何影响计算值的值发生变化了,计算值将根据状态自动进行衍生。
@computed
如果已经启用 decorators 的话,可以在任意类属性的 getter 上使用 @computed 装饰器来声明式的创建计算属性。
1 | import { observable, computed } from "mobx"; |
计算值的 setter
可以为计算值定义 setter。这些 setters 不能用来直接改变计算属性的值,但是它们可以用来作“逆向”衍生。
1 | const orderLine = observable.object({ |
computed(expression) 函数
computed 还可以直接当做函数来调用。 就像 observable.box(primitive value) 创建一个独立的 observable。 在返回的对象上使用 .get() 来获取计算的当前值,或者使用 .observe(callback) 来观察值的改变。
computed 的选项
当使用 computed 作为调节器或者盒子,它接收的第二个选项参数对象,选项参数对象有如下可选参数:
- name: 字符串, 在 spy 和 MobX 开发者工具中使用的调试名称
- context: 在提供的表达式中使用的 this
- set: 要使用的 setter 函数。 没有 setter 的话无法为计算值分配新值。 如果传递给 computed 的第二个参数是一个函数,那么就把会这个函数作为 setter
- equals: 默认值是 comparer.default 。它充当比较前一个值和后一个值的比较函数。如果这个函数认为前一个值和后一个值是相等的,那么观察者就不会重新评估。这在使用结构数据和来自其他库的类型时很有用。例如,一个 computed 的 moment 实例可以使用 (a, b) => a.isSame(b) 。如果想要使用结构比较来确定新的值是否与上个值不同 (并作为结果通知观察者),comparer.deep 十分便利。
- requiresReaction: 对于非常昂贵的计算值,推荐设置成 true 。如果你尝试读取它的值,但某些观察者没有跟踪该值(在这种情况下,MobX 不会缓存该值),则会导致计算结果丢失,而不是进行昂贵的重新评估。
- keepAlive: 如果没有任何人观察到,则不要使用此计算值。 请注意,这很容易导致内存泄漏,因为它会导致此计算值使用的每个 observable ,并将计算值保存在内存中!
错误处理
如果计算值在其计算期间抛出异常,则此异常将捕获并在读取其值时重新抛出。
1 | const x = observable.box(3); |
Autorun
当你想创建一个响应式函数,而该函数本身永远不会有观察者时,可以使用 mobx.autorun。相比之下,computed(function) 创建的函数只有当它有自己的观察者时才会重新计算,否则它的值会被认为是不相关的。
如果你有一个函数应该自动运行,但不会产生一个新的值,请使用 autorun。其余情况都应该使用 computed。
Autoruns 是关于 启动效果 (initiating effects) 的 ,而不是产生新的值。
如果字符串作为第一个参数传递给 autorun ,它将被用作调试名。
Autorun 接收第二个参数,它是一个参数对象,有如下可选的参数:
- delay: 可用于对效果函数进行去抖动的数字(以毫秒为单位)。如果是 0(默认值) 的话,那么不会进行去抖。
- name: 字符串,用于在例如像 spy 这样事件中用作此 reaction 的名称。
- onError: 用来处理 reaction 的错误,而不是传播它们。
- scheduler: 设置自定义调度器以决定如何调度 autorun 函数的重新运行。
delay 选项
1 | autorun( |
onError 选项
在 autorun 和所有其他类型 reaction 中抛出的异常会被捕获并打印到控制台,但不会将异常传播回原始导致异常的代码。 这是为了确保一个异常中的 reaction 不会阻止其他可能不相关的 reaction 的预定执行。 这也允许 reaction 从异常中恢复; 抛出异常不会破坏 MobX 的跟踪,因此如果除去异常的原因,reaction 的后续运行可能会再次正常完成。
可以通过提供 onError 选项来覆盖 Reactions 的默认日志行为。 示例:
1 | const age = observable.box(10); |
when
when 观察并运行给定的 predicate,直到返回 true。 一旦返回 true,给定的 effect 就会被执行,然后 autorunner(自动运行程序) 会被清理。 该函数返回一个清理器以提前取消自动运行程序。
对于以响应式方式来进行处理或者取消,此函数非常有用。 示例:
1 | class MyResource { |
Reaction
1 | reaction(() => data, (data, reaction) => { sideEffect }, options?) |
autorun 的变种,对于如何追踪 observable 赋予了更细粒度的控制。 它接收两个函数参数,第一个(数据函数)是用来追踪并返回数据作为第二个函数(效果函数)的输入。
Reaction 接收第三个参数,它是一个参数对象,有如下可选的参数:
- fireImmediately: 布尔值,用来标识效果函数是否在数据函数第一次运行后立即触发。默认值是 false 。
- delay: 可用于对效果函数进行去抖动的数字(以毫秒为单位)。如果是 0(默认值) 的话,那么不会进行去抖。
- equals: 默认值是 comparer.default 。如果指定的话,这个比较器函数被用来比较由 数据 函数产生的前一个值和后一个值。只有比较器函数返回 false 效果 函数才会被调用。此选项如果指定的话,会覆盖 compareStructural 选项。
- name: 字符串,用于在例如像 spy 这样事件中用作此 reaction 的名称。
- onError: 用来处理 reaction 的错误,而不是传播它们。
- scheduler: 设置自定义调度器以决定如何调度 autorun 函数的重新运行
在下面的示例中,reaction1、reaction2 和 autorun1 都会对 todos 数组中的 todo 的添加、删除或替换作出反应。 但只有 reaction2 和 autorun 会对某个 todo 的 title 变化作出反应,因为在 reaction2 的数据表达式中使用了 title,而 reaction1 的数据表达式没有使用。
1 | const todos = observable([ |
@observer
observer 函数/装饰器可以用来将 React 组件转变成响应式组件。 它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件。
observer 是由单独的 mobx-react 包提供的。
1 | import { observer } from "mobx-react"; |
可观察的局部组件状态
就像普通类一样,你可以通过使用 @observable 装饰器在 React 组件上引入可观察属性。 这意味着你可以在组件中拥有功能同样强大的本地状态(local state),而不需要通过 React 的冗长和强制性的 setState 机制来管理。
1 | import { observer } from "mobx-react"; |
使用 inject 将组件连接到提供的 stores
mobx-react 包还提供了 Provider 组件,它使用了 React 的上下文(context)机制,可以用来向下传递 stores。 要连接到这些 stores,需要传递一个 stores 名称的列表给 inject,这使得 stores 可以作为组件的 props 使用。
1 | const colors = observable({ |
componentWillReact (生命周期钩子)
由 mobx-react 提供。当组件因为它观察的数据发生了改变,它会安排重新渲染,这个时候 componentWillReact 会被触发。
1 | import { observer } from "mobx-react"; |
componentWillReact 初始化渲染前不会触发 (使用 componentWillMount 替代)
componentWillReact 对于 mobx-react@4+, 当接收新的 props 时并在 setState 调用后会触发此钩子
MobX 会对什么作出反应?
MobX 会对在追踪函数执行过程中读取现存的可观察属性做出反应。
- “读取” 是对象属性的间接引用,可以用过 . (例如 user.name) 或者 [] (例如 user[‘name’]) 的形式完成。
- “追踪函数” 是 computed 表达式、observer 组件的 render() 方法和 when、reaction 和 autorun 的第一个入参函数。
- “过程(during)” 意味着只追踪那些在函数执行时被读取的 observable 。这些值是否由追踪函数直接或间接使用并不重要。
换句话说,MobX 不会对其作出反应:
- 从 observable 获取的值,但是在追踪函数之外
- 在异步调用的代码块中读取的 observable