MobX 🇺🇦

MobX 🇺🇦

  • API 参考
  • 中文
  • 한국어
  • 赞助商
  • GitHub

›MobX 核心

介绍

  • 关于 MobX
  • 关于此文档
  • 安装
  • MobX 的核心

MobX 核心

  • 可观察状态
  • Action
  • 计算属性
  • 反应 {🚀}
  • API

MobX 和 React

  • React 集成
  • React 优化 {🚀}

提示和技巧

  • 定义数据存储
  • 了解反应性
  • 子类化
  • 分析反应性 {🚀}
  • 带有参数的计算属性 {🚀}
  • MobX-utils {🚀}
  • 自定义可观察对象 {🚀}
  • 延迟可观察对象 {🚀}
  • 集合工具 {🚀}
  • 拦截和观察 {🚀}

微调

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

使用 action 更新状态

用法

  • action (注解)
  • action(fn)
  • action(name, fn)
  • @action (方法/字段装饰器)

所有应用程序都有 action。action 是任何修改状态的代码段。原则上,action 总是响应事件而发生。例如,点击按钮、更改输入、收到 websocket 消息等。

MobX 要求您声明 action,尽管 makeAutoObservable 可以自动化许多工作。action 帮助您更好地构建代码,并提供以下性能优势:

  1. 它们在 事务 内运行。在最外层的 action 完成之前,不会运行任何反应,保证在 action 期间产生的中间值或不完整值在 action 完成之前不会对应用程序的其余部分可见。

  2. 默认情况下,不允许在 action 之外更改状态。这有助于在您的代码库中清晰地识别状态更新发生的位置。

action 注解仅应用于打算修改状态的函数。推导出信息(执行查找或过滤数据)的函数不应标记为 action,以允许 MobX 跟踪它们的调用。action 注释的成员将不可枚举。

示例

makeObservable
@action
makeAutoObservable
action.bound
action(fn)
runInAction(fn)
import { makeObservable, observable, action } from "mobx"

class Doubler {
value = 0

constructor() {
makeObservable(this, {
value: observable,
increment: action
})
}

increment() {
// Intermediate states will not become visible to observers.
this.value++
this.value++
}
}
import { observable, action } from "mobx"

class Doubler {
@observable accessor value = 0

@action
increment() {
// Intermediate states will not become visible to observers.
this.value++
this.value++
}
}
import { makeAutoObservable } from "mobx"

class Doubler {
value = 0

constructor() {
makeAutoObservable(this)
}

increment() {
this.value++
this.value++
}
}
import { makeObservable, observable, action } from "mobx"

class Doubler {
value = 0

constructor() {
makeObservable(this, {
value: observable,
increment: action.bound
})
}

increment() {
this.value++
this.value++
}
}

const doubler = new Doubler()

// Calling increment this way is safe as it is already bound.
setInterval(doubler.increment, 1000)
import { observable, action } from "mobx"

const state = observable({ value: 0 })

const increment = action(state => {
state.value++
state.value++
})

increment(state)
import { observable, runInAction } from "mobx"

const state = observable({ value: 0 })

runInAction(() => {
state.value++
state.value++
})

使用 action 包装函数

为了尽可能地利用 MobX 的事务特性,action 应该尽可能地向外传递。如果一个类方法修改了状态,最好将其标记为 action。标记事件处理程序为 action 甚至更好,因为最外层的事务才是最重要的。单个未标记的事件处理程序,如果随后调用两个 action,仍然会生成两个事务。

为了帮助创建基于 action 的事件处理程序,action 不仅是一个注解,也是一个高阶函数。它可以接受一个函数作为参数,在这种情况下,它将返回一个具有相同签名的 action 包装函数。

例如,在 React 中,一个 onClick 处理程序可以像下面这样被包装。

const ResetButton = ({ formState }) => (
    <button
        onClick={action(e => {
            formState.resetPendingUploads()
            formState.resetValues()
            e.preventDefault()
        })}
    >
        Reset form
    </button>
)

为了调试的目的,我们建议您命名包装的函数,或者将名称作为第一个参数传递给 action。

注意: action 是未跟踪的

action 的另一个特性是它们是 未跟踪的。当从副作用或计算属性(非常罕见!)内部调用一个 action 时,action 读取的可观察对象不会计入派生依赖项。

makeAutoObservable、extendObservable 和 observable 使用一种特殊的 action,称为 autoAction,它将在运行时确定函数是派生函数还是 action。

action.bound

用法

  • action.bound (注解)

action.bound 注解可用于自动将方法绑定到正确的实例,以便 this 始终在函数内部被正确绑定。

提示: 使用 makeAutoObservable(o, {}, { autoBind: true }) 自动绑定所有 action 和流程

import { makeAutoObservable } from "mobx"

class Doubler {
    value = 0

    constructor() {
        makeAutoObservable(this, {}, { autoBind: true })
    }

    increment() {
        this.value++
        this.value++
    }

    *flow() {
        const response = yield fetch("http://example.com/value")
        this.value = yield response.json()
    }
}

runInAction

用法

  • runInAction(fn)

使用此实用程序来创建一个立即调用的临时 action。在异步进程中可能很有用。查看上面的代码块以获取示例。

Action 和继承

仅定义在原型上的 action 可以被子类覆盖

class Parent {
    // on instance
    arrowAction = () => {}

    // on prototype
    action() {}
    boundAction() {}

    constructor() {
        makeObservable(this, {
            arrowAction: action
            action: action,
            boundAction: action.bound,
        })
    }
}
class Child extends Parent {
    // THROWS: TypeError: Cannot redefine property: arrowAction
    arrowAction = () => {}

    // OK
    action() {}
    boundAction() {}

    constructor() {
        super()
        makeObservable(this, {
            arrowAction: override,
            action: override,
            boundAction: override,
        })
    }
}

要将单个action 绑定到 this,可以使用 action.bound 代替箭头函数。
有关更多信息,请参阅子类化。

异步 action

从本质上讲,异步进程不需要在 MobX 中进行任何特殊处理,因为所有反应都会自动更新,无论它们是在什么时间点触发的。由于可观察对象是可变的,因此通常可以安全地保留对它们的引用,以供 action 的持续时间使用。但是,异步进程中更新可观察对象的每个步骤(滴答)都应标记为 action。这可以通过多种方式实现,利用上面的 API,如下所示。

例如,在处理 promise 时,更新状态的处理程序应该是 action,或者应该使用 action 包装,如下所示。

在 action 中包装处理程序
在单独的 action 中处理更新
async/await + runInAction
flow + 生成器函数

promise 解析处理程序是在行内处理的,但在原始 action 完成后运行,因此需要用 action 包装它们

import { action, makeAutoObservable } from "mobx"

class Store {
githubProjects = []
state = "pending" // "pending", "done" or "error"

constructor() {
makeAutoObservable(this)
}

fetchProjects() {
this.githubProjects = []
this.state = "pending"
fetchGithubProjectsSomehow().then(
action("fetchSuccess", projects => {
const filteredProjects = somePreprocessing(projects)
this.githubProjects = filteredProjects
this.state = "done"
}),
action("fetchError", error => {
this.state = "error"
})
)
}
}

如果 promise 处理程序是类字段,它们将被 makeAutoObservable 自动包装在 action 中

import { makeAutoObservable } from "mobx"

class Store {
githubProjects = []
state = "pending" // "pending", "done" or "error"

constructor() {
makeAutoObservable(this)
}

fetchProjects() {
this.githubProjects = []
this.state = "pending"
fetchGithubProjectsSomehow().then(this.projectsFetchSuccess, this.projectsFetchFailure)
}

projectsFetchSuccess = projects => {
const filteredProjects = somePreprocessing(projects)
this.githubProjects = filteredProjects
this.state = "done"
}

projectsFetchFailure = error => {
this.state = "error"
}
}

await 之后的任何步骤都不在同一个滴答中,因此它们需要 action 包装。在这里,我们可以利用 runInAction

import { runInAction, makeAutoObservable } from "mobx"

class Store {
githubProjects = []
state = "pending" // "pending", "done" or "error"

constructor() {
makeAutoObservable(this)
}

async fetchProjects() {
this.githubProjects = []
this.state = "pending"
try {
const projects = await fetchGithubProjectsSomehow()
const filteredProjects = somePreprocessing(projects)
runInAction(() => {
this.githubProjects = filteredProjects
this.state = "done"
})
} catch (e) {
runInAction(() => {
this.state = "error"
})
}
}
}
import { flow, makeAutoObservable, flowResult } from "mobx"

class Store {
githubProjects = []
state = "pending"

constructor() {
makeAutoObservable(this, {
fetchProjects: flow
})
}

// Note the star, this a generator function!
*fetchProjects() {
this.githubProjects = []
this.state = "pending"
try {
// Yield instead of await.
const projects = yield fetchGithubProjectsSomehow()
const filteredProjects = somePreprocessing(projects)
this.state = "done"
this.githubProjects = filteredProjects
return projects
} catch (error) {
this.state = "error"
}
}
}

const store = new Store()
const projects = await flowResult(store.fetchProjects())

使用 flow 代替 async / await {🚀}

用法

  • flow (注解)
  • flow(function* (args) { })
  • @flow (方法装饰器)

flow 包装器是 async / await 的可选替代方案,它使使用 MobX action 变得更容易。flow 接受一个 生成器函数 作为其唯一的输入。在生成器内部,您可以通过 yield promise 来链接 promise(而不是 await somePromise,您写 yield somePromise)。然后,flow 机制将确保生成器在 yield promise 解析时继续或抛出。

所以 flow 是 async / await 的替代方案,它不需要任何进一步的 action 包装。它可以按以下方式应用

  1. 将 flow 包装在您的异步函数周围。
  2. 不要使用 async,而是使用 function *。
  3. 不要使用 await,而是使用 yield。

上面的 flow + 生成器函数 示例显示了这在实践中是如何实现的。

请注意,flowResult 函数仅在使用 TypeScript 时才需要。由于用 flow 装饰了一个方法,它将把返回的生成器包装在一个 promise 中。但是,TypeScript 并不知道这种转换,因此 flowResult 将确保 TypeScript 了解这种类型更改。

makeAutoObservable 及其朋友将自动推断生成器为 flow。flow 注释的成员将不可枚举。

{🚀} 注意: 在对象字段上使用 flow flow 与 action 一样,可以用来直接包装函数。上面的示例也可以这样写

import { flow, makeObservable, observable } from "mobx"

class Store {
    githubProjects = []
    state = "pending"

    constructor() {
        makeObservable(this, {
            githubProjects: observable,
            state: observable,
        })
    }

    fetchProjects = flow(function* (this: Store) {
        this.githubProjects = []
        this.state = "pending"
        try {
            // yield instead of await.
            const projects = yield fetchGithubProjectsSomehow()
            const filteredProjects = somePreprocessing(projects)
            this.state = "done"
            this.githubProjects = filteredProjects
        } catch (error) {
            this.state = "error"
        }
    })
}

const store = new Store()
const projects = await store.fetchProjects()

好处是,我们不再需要 flowResult,坏处是 this 需要进行类型化,以确保其类型被正确推断。

flow.bound

用法

  • flow.bound (注解)

flow.bound 注解可以用于自动将方法绑定到正确的实例,这样 this 始终在函数内部正确绑定。类似于操作,流可以通过 autoBind 选项 默认绑定。

取消流 {🚀}

流的另一个好处是它们可以取消。flow 的返回值是一个 Promise,它在生成器函数最终返回的值解析。返回的 Promise 有一个额外的 cancel() 方法,它将中断正在运行的生成器并取消它。任何 try / finally 子句仍将运行。

禁用强制操作 {🚀}

默认情况下,MobX 6 及更高版本要求您使用操作来更改状态。但是,您可以配置 MobX 来禁用此行为。查看 enforceActions 部分。例如,这在单元测试设置中非常有用,因为警告并不总是具有太多价值。

← 可观察状态计算值 →
  • 示例
  • 使用 action 包装函数
  • action.bound
  • runInAction
  • 操作和继承
  • 异步操作
  • 使用流代替 async / await {🚀}
  • flow.bound
  • 取消流 {🚀}
  • 禁用强制操作 {🚀}
MobX 🇺🇦
文档
关于 MobXMobX 的要点
社区
GitHub 讨论 (NEW)Stack Overflow
更多
点赞