自学内容网 自学内容网

【前端知识】简单,可扩展的状态管理组件MobX

概念

MobX区分了应用程序中的以下三个概念:

  • State(状态)
  • Actions(动作)
  • Derivations(派生)
    让我们仔细看看下面的这些概念,或者在10分钟的MobX和React简介中,您可以通过交互方式逐步深入了解这些概念,并构建一个简单的待办事项列表(Todo List)应用程序。

1. 定义 State 并使其可观察

State(状态) 是驱动你的应用程序的数据。

通常来说,状态有_领域特定状态_(比如 Todo List 中的列表项数据)和_视图状态_ (比如当前选中的列表元素)。State 就像保存着数据的电子表格单元格一样。

将 State 存储在任何您喜欢的数据结构中:普通对象、数组、类、循环数据结构或引用。这与MobX的工作方式无关。

只要确保所有你想随时间改变的属性都被标记为observable,这样MobX就可以跟踪它们。

以下是一个简单的示例:

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

class Todo {
    id = Math.random()
    title = ""
    finished = false

    constructor(title) {
        makeObservable(this, {
            title: observable,
            finished: observable,
            toggle: action
        })
        this.title = title
    }

    toggle() {
        this.finished = !this.finished
    }
}
  • 提示: 在这个例子中我们可以用 makeAutoObservable 对其进行简化,但是为了能更详细的展示不同的概念,我们对其进行显式设置。

使用 observable 就像将对象的属性放在Excel表格的单元格中。但是和单元格不同的是,他们的值不仅仅是数值,也可以是引用、对象和数组。

接下来我们看一下被我们标记为 actiontoggle

2. 使用 Action 更新 State

Action(动作) 是任意可以改变 State(状态) 的代码,比如用户事件处理、后端推送数据处理、调度器事件处理等等。

Action 就像用户在Excel单元格中输入了新的值。

Todo 类中,我们可以看到 toggle 方法改变了 finished 属性的值,而 finished 是被标记为 observable 的。建议您将所有修改 observable 值的代码标记为 action。MobX 可以自动进行事务处理以轻松实现最佳性能。

使用 Action 可以帮助您更好地组织代码,并防止您在无意中修改 State。

在 MobX 术语中,可以修改 State 的方法被称为 action(动作) 。这与基于当前状态来生成新信息的 view(视图) 是不同的。 您代码中的每一个方法只应完成上述两个目标中的一个。

3. 创建 Derivations 以便自动对 State 变化进行响应

任何 来源是_State(状态)_ 并且不需要进一步交互的东西都是 Derivation(派生)。

Derivations 包括许多方式:
  • 用户界面
  • 派生数据 , 比如剩余未完成todos的数量
  • 后端集成 , 比如发送改变到服务器端
Mobx 区分了两种 Derivation :
  • Computed values,总是可以通过纯函数从当前的可观测 State 中派生。
  • Reactions, 当 State 改变时需要自动运行的副作用 (命令式编程和响应式编程之间的桥梁)
    当最开始使用MobX时,人们容易过度使用 Reaction。

黄金法则是,如果要基于当前 State 创建值,请始终使用 computed。

3.1. 通过 computed 对派生值进行建模

你可以通过定义 getter 方法并使用 makeObservable 将其标记为 computed 的方式创建一个 computed 值。


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

class TodoList {
    todos = []
    get unfinishedTodoCount() {
        return this.todos.filter(todo => !todo.finished).length
    }
    constructor(todos) {
        makeObservable(this, {
            todos: observable,
            unfinishedTodoCount: computed
        })
        this.todos = todos
    }
}

Mobx 会确保 unfinishedTodoCount 会在todos数组发生变化中或者 todos中的一个对象中的 finished属性被修改时自动更新。

这些计算类似于 Excel 单元格中的公式。它们仅在需要时自动更新。也就是说,如果有观察者使用其结果时才会更新。也就是说,如果有有人关心其结果时才会更新。

3.2. 使用 reaction 对副作用建模

作为用户,要想在屏幕上看到状态或计算值的变化,就需要一个重新绘制部分GUI的 reactions 。

Reaction 和 computed 类似,但并不产生信息,而是产生副作用,如打印到控制台、发出网络请求、增量更新 React 组件树以便更新DOM等。

简而言之,reaction 是 响应式编程和指令式编程之间的桥梁。

到目前为止,最常用的 reaction 形式是UI组件。 注意,action 和 reaction 都可能引起副作用。 副作用应有一个清晰的、显式的起源,例如在提交表单时发出网络请求,应该从相关的事件处理程序显式触发。

3.3. 响应式 React 组件

如果使用 React,你可以将组件用安装中下载的包中的observer函数来包装起来,以便让组件成为响应式的。在这个示例中,我们将用更轻量的 mobx-react-lite 包。

import * as React from "react"
import { render } from "react-dom"
import { observer } from "mobx-react-lite"

const TodoListView = observer(({ todoList }) => (
    <div>
        <ul>
            {todoList.todos.map(todo => (
                <TodoView todo={todo} key={todo.id} />
            ))}
        </ul>
        Tasks left: {todoList.unfinishedTodoCount}
    </div>
))

const TodoView = observer(({ todo }) => (
    <li>
        <input type="checkbox" checked={todo.finished} onClick={() => todo.toggle()} />
        {todo.title}
    </li>
))

const store = new TodoList([new Todo("Get Coffee"), new Todo("Write simpler code")])
render(<TodoListView todoList={store} />, document.getElementById("root"))

observer 将 React 组件转化为了从数据到渲染的派生过程。 当使用 MobX 时,不存在“智能组件”和“木偶组件”。所有的组件在渲染时都是智能的,但是在定义时是按照木偶组件的方式去定义的。MobX会简单确定这个组件是否需要进行重绘,并止步于此。

因此,上述示例中的onClick事件处理器调用toggle Action 后,会使对应的TodoView组件重绘,但仅当未完成任务的数量发生更改时才会使 TodoListView 组件重绘。

如果移除了Tasks left这行代码(或者把他拆分到另一个组件中),TodoListView组件就不再 toggle 执行时产生重绘了。

您可以查阅与 React 集成来了解更多有关 React 是如何与 MobX 协同运作的。

3.4. 自定义 Reaction

通常情况下你不需要使用它们,可以使用 autorun ,reaction 或 when 方法来订制你的特殊业务场景。

比如,下面的 autorun 将在unfinishedTodoCount的数量发生变化时输出日志。

// 一个自动观察state的函数
autorun(() => {
    console.log("Tasks left: " + todos.unfinishedTodoCount)
})

为什么每次 unfinishedTodoCount 发生改变时都会输出日志信息呢?答案是以下法则:

MobX对在执行跟踪函数期间读取的任何现有可观察属性作出响应。

要了解更多关于MobX如何确定需要对哪些可观察对象作出响应的信息,请查看 理解响应性 章节。

原则
Mobx 使用单向数据流,利用 action 改变 state ,进而更新所有受影响的 view

state-change-view

  • 所有的 derivations 将在 state 改变时自动且原子化地更新。因此不可能观察中间值。
  • 所有的 derivations 默认将会同步更新,这意味着 action 可以在 state 改变 之后安全的直接获得 computed 值。
  • computed value 的更新是惰性的,任何 computed value 在需要他们的副作用发生之前都是不激活的。
  • 所有的 computed value 都应是纯函数,他们不应该修改 state

API参考

MobX是一个强大的状态管理库,其API提供了丰富的功能来支持响应式编程。以下是对MobX API的详细参考:

一、核心API

  1. observable

    • 功能:将对象、数组、Map、Set等数据结构转换为可观察的状态。
    • 用法
      • observable(source, overrides?, options?):克隆一个对象并把它转化成observable。source可以是一个普通的对象、数组、Map或Set。
      • observable.object(source, overrides?, options?)observable的别名,创建一个被传入对象的副本并使它的所有属性observable。
      • observable.array(initialValues?, options?):基于所提供的initialValues创建一个新的observable数组。
      • observable.map(initialMap?, options?):基于所提供的initialMap创建一个新的observable ES6 Map。
      • observable.set(initialSet?, options?):根据所提供的initialSet创建一个新的observable ES6 Set。
      • observable.ref(annotation):和observable注解类似,但只有重新赋值会被追踪。所赋的值本身并不会被自动转化成observable。
      • observable.shallow(annotation):和observable.ref注解类似,但用于集合。任何所赋的集合都会被转化成observable,但是集合本身的内容不会被转化成observable。
      • observable.struct(annotation):和observable注解类似,但是与现有值结构相等的任何赋值都会被忽略。
      • observable.deep(annotation)observable注解的别名。
      • observable.box(value, options?):JavaScript中的所有原始值都是不可变的,所以它们都不是observable。可以创建一个observable box来管理这种原始值。
  2. makeObservable

    • 功能:将目标对象中的属性和方法设置为observable state和action。
    • 用法makeObservable(target, annotations?, options?)
  3. makeAutoObservable

    • 功能:自动将属性、对象、数组、Maps和Sets转化成observable,并同时处理actions和computed。
    • 用法makeAutoObservable(target, overrides?, options?)
  4. extendObservable

    • 功能:在target对象上引入新属性并立即把它们全部转化成observable。
    • 用法extendObservable(target, properties, overrides?, options?)

二、其他API

MobX还提供了许多其他API来支持更复杂的场景和调试需求,如reactionautorunwhenconfigure等。这些API在MobX的官方文档中都有详细的介绍和示例。

三、使用示例

以下是一个简单的使用示例,展示了如何使用MobX来管理React组件的状态:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { makeAutoObservable } from 'mobx';
import { Provider, observer } from 'mobx-react';

// 定义一个store来管理状态
class Store {
  count = 0;

  constructor() {
    makeAutoObservable(this);
  }

  increment() {
    this.count++;
  }
}

const store = new Store();

// 定义一个响应式组件
const Counter = observer(() => {
  return (
    <div>
      <p>Count: {store.count}</p>
      <button onClick={() => store.increment()}>Increment</button>
    </div>
  );
});

// 使用Provider将store提供给组件树
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
  <Provider store={store}>
    <Counter />
  </Provider>
);

在这个示例中,我们定义了一个Store类来管理状态,并使用makeAutoObservable来将其属性和方法转换为observable和action。然后,我们定义了一个响应式组件Counter,它使用observer高阶组件来订阅store的状态变化。最后,我们使用Provider将store提供给组件树,这样Counter组件就可以访问和响应store的状态变化了。

总的来说,MobX的API提供了强大的功能来支持响应式编程和状态管理,使得开发者可以更加高效地构建和维护复杂的React应用。


原文地址:https://blog.csdn.net/wendao76/article/details/144302652

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!