MobX 中文网

MobX 中文网

  • API 文档
  • Nodejs.cn 旗下网站

›提示与技巧

介绍

  • 关于 MobX
  • 关于本文档
  • 安装
  • MobX 的要点

MobX 核心

  • 可观察状态
  • 操作
  • 计算
  • 反应 {🚀}
  • API

MobX 与 React

  • React 集成
  • React 优化 {🚀}

提示与技巧

  • 定义数据存储
  • 了解反应性
  • 子类化
  • 分析反应性 {🚀}
  • 使用参数进行计算 {🚀}
  • MobX-utils {🚀}
  • 自定义可观察值 {🚀}
  • 惰性可观察量 {🚀}
  • 集合实用程序 {🚀}
  • 拦截和观察 {🚀}

微调

  • 配置 {🚀}
  • 装饰器 {🚀}
  • 从 MobX 4/5 迁移 {🚀}

使用参数进行计算 {🚀}

¥Computeds with arguments {🚀}

computed 注释只能用于不带参数的 getter。带有参数的计算怎么样?以下面的 React 组件为例,该组件渲染特定的 Item,并且该应用支持多选。

¥The computed annotation can only be used on getters, which don't take arguments. What about computations that do take arguments? Take the below example of a React component that renders a specific Item, and the application supports multi-selection.

我们怎样才能实现像 store.isSelected(item.id) 这样的推导呢?

¥How can we implement a derivation like store.isSelected(item.id)?

import * as React from 'react'
import { observer } from 'mobx-react-lite'

const Item = observer(({ item, store }) => (
    <div className={store.isSelected(item.id) ? "selected" : ""}>
        {item.title}
    </div>
))

我们可以通过四种方式来解决这个问题。你可以在 这个代码沙盒 中尝试以下解决方案。

¥There are four ways in which we can approach this. You can try the solutions below in this CodeSandbox.

1. 导数不必是 computed

¥ Derivations don't need to be computed

函数不需要标记为 computed 即可让 MobX 跟踪它。上面的例子已经可以完全正常工作了。重要的是要认识到计算值只是缓存点。如果派生是纯粹的(而且应该是),那么拥有一个没有 computed 的 getter 或函数不会改变行为,只是效率稍低一些。

¥A function doesn't need to be marked as computed in order for MobX to track it. The above example would already work completely fine out of the box. It is important to realize that computed values are only caching points. If the derivations are pure (and they should be), having a getter or function without computed doesn't change the behavior, it is just slightly less efficient.

尽管 isSelected 不是 computed,上面的示例仍然可以正常工作。observer 组件将检测并订阅 isSelected 读取的任何可观察量,因为该函数作为跟踪的渲染的一部分执行。

¥The above example works fine despite isSelected not being a computed. The observer component will detect and subscribe to any observables that were read by isSelected because the function executes as part of rendering that is tracked.

在这种情况下,最好认识到所有 Item 组件都会响应未来的选择更改,因为它们都直接订阅捕获选择的可观察量。这是一个最坏情况的例子。一般来说,使用未标记的函数来导出信息是完全可以的,这是一个很好的默认策略,直到数字证明应该做其他事情。

¥It is good to realize that all Item components, in this case, will respond to future selection changes, as they all subscribe directly to the observables that capture the selection. This is a worst-case example. In general, it is completely fine to have unmarked functions that derive information, and this is a good default strategy until numbers prove anything else should be done.

2. 结束争论

¥ Close over the arguments

与原始实现相比,这是一个更有效的实现。

¥This is a more efficient implementation compared to the original.

import * as React from 'react'
import { computed } from 'mobx'
import { observer } from 'mobx-react-lite'

const Item = observer(({ item, store }) => {
    const isSelected = computed(() => store.isSelected(item.id)).get()
    return (
        <div className={isSelected ? "selected" : ""}>
            {item.title}
        </div>
    )
})

我们在反应过程中创建一个新的计算值。这工作得很好,并且确实引入了额外的缓存点,避免了所有组件都必须直接响应每个选择更改。这种方法的优点是组件本身只会在 isSelected 状态切换时重新渲染,在这种情况下我们确实必须重新渲染以交换 className。

¥We create a fresh computed value in the middle of a reaction. This works fine and does introduce that additional caching point, avoiding all components having to directly respond to every selection change. The advantage of this approach is that the component itself will only re-render if the isSelected state toggles, in which case we indeed have to re-render to swap the className.

事实上,我们在下一个渲染中创建一个新的 computed 很好,这个现在将成为缓存点,而前一个将被很好地清理。这是一项很棒而先进的优化技术。

¥The fact that we create a new computed in a next render is fine, this one will now become the caching point and the previous one will be cleaned up nicely. This is a great and advanced optimization technique.

3. 移动状态

¥ Move the state

在这种特定情况下,选择也可以作为 isSelected 可观测值存储在 Item 上。存储中的选择可以表示为 computed 而不是可观察的:get selection() { return this.items.filter(item => item.isSelected) },我们不再需要 isSelected 了。

¥In this specific case the selection could also be stored as an isSelected observable on the Item. The selection in the store could then be expressed as a computed rather than an observable: get selection() { return this.items.filter(item => item.isSelected) }, and we don't need isSelected anymore.

4. 使用 computedFn {🚀}

¥ Use computedFn {🚀}

最后,可以在 todoStore.selected 的定义中使用 mobx-utils 中的 computedFn 来自动记忆 isSelected。它创建一个函数来记忆每个输入参数组合的输出。

¥Finally, computedFn from mobx-utils can be used in the definition of todoStore.selected to automatically memoize isSelected. It creates a function that memoizes the output for every combination of input arguments.

我们建议不要太快诉诸这一方法。对于记忆来说,这是典型的,在推断内存消耗之前,你需要考虑将使用多少个不同的参数来调用该函数。然而,如果没有任何反应观察到条目的结果,它会自动清理条目,因此在正常情况下不会泄漏内存。

¥We recommend to not resort to this one too quickly. It is typical for memoization, that you will need to think about how many different arguments the function is going to be called with, before you can reason about the memory consumption. It does however automatically clean up entries if their results aren't observed by any reaction, so it won't leak memory in normal circumstances.

再次检查 链接的 CodeSandbox 来尝试一下这个。

¥Again, check out the linked CodeSandbox to try this one out.

← 分析反应性 {🚀}MobX-utils {🚀} →
  • 1. 导数不必是 computed
  • 2. 结束争论
  • 3. 移动状态
  • 4. 使用 computedFn {🚀}
MobX v6.13 中文网 - 粤ICP备13048890号
Nodejs.cn 旗下网站