MobX 是一款通过透明地应用函数式响应式编程(FRP)来使状态管理变得简单和可扩展的库。在 MobX 6 中,引入了一些新的功能和改进,其中包括 autoRun、reaction 和 runInAction。下面我们将逐一深入探讨这些功能,并通过代码示例来展示它们的使用方法和优势。
一、autoRun
autoRun 是 MobX 6 中的一个新功能,它允许你自动跟踪响应式数据的变化,并在这些数据发生变化时运行特定的函数。autoRun 返回一个处理函数,可以用来停止或重新启动反应。
下面是一个使用 autoRun 的简单示例:
import { observable, autoRun, enableLogging } from 'mobx';
enableLogging(); // 开启日志,便于观察状态变化
const store = observable({
count: 0
});
const disposer = autoRun(() => {
console.log(`Count has changed: ${store.count}`);
});
// 改变状态,会触发 autoRun 中的函数
store.count = 1; // 输出:Count has changed: 1
// 当不再需要反应时,可以使用 disposer 函数来停止它
disposer();
store.count = 2; // 无输出,因为反应已被停止
在这个例子中,我们创建了一个可观察的对象 store,并使用 autoRun 来自动跟踪 store.count 的变化。当我们改变 store.count 的值时,autoRun 中的函数会自动执行。
二、reaction
reaction 是 MobX 中用于创建响应式反应的另一个重要函数。与 autoRun 不同,reaction 允许你定义一个更复杂的反应逻辑,并且只在该逻辑的结果发生变化时触发。
下面是一个使用 reaction 的示例:
import { observable, reaction } from 'mobx';
const store = observable({
todos: []
});
reaction(
() => store.todos.length, // 跟踪 todos 长度的变化
(length, reaction) => {
console.log(`Todo count changed: ${length}`);
if (length > 0) {
console.log("There are todos to complete!");
} else {
console.log("No todos left!");
}
}
);
// 添加和删除 todos,观察控制台的输出变化
store.todos = [{ text: 'Learn MobX' }]; // 输出:Todo count changed: 1,以及 "There are todos to complete!"
store.todos = []; // 输出:Todo count changed: 0,以及 "No todos left!"
在这个例子中,我们使用 reaction 来跟踪 store.todos 数组的长度的变化,并在长度发生变化时执行特定的逻辑。
三、runInAction
runInAction 是 MobX 6 中的一个重要功能,它允许你在一个单一的、原子的、同步的事务中执行多个状态更改。这可以确保在更改过程中不会触发任何观察者,直到所有更改都完成为止。
下面是一个使用 runInAction 的示例:
import { observable, runInAction } from 'mobx';
const store = observable({
count1: 0,
count2: 0
});
runInAction(() => {
store.count1 = 1;
store.count2 = 2;
});
// 在这个 runInAction 块中,任何观察者都不会被触发,直到整个块执行完毕。
在这个例子中,我们使用 runInAction 来确保在更改 count1 和 count2 时不会触发任何中间状态的观察者。
再比如,向被监控的数组中添加元素的时候,可以使用 runInAction 来避免组件的多次渲染:
async loadTodos() {
let todos = await axios.get("http://localhost:3005/todos").then(response => response.data);
runInAction(() => todos.forEach(todo => this.todos.push(todo)))
}
四、对比 runInAction 和之前使用 generator 实现相同功能的代码差异
在 MobX 5 及更早版本中,为了实现类似 runInAction 的功能,我们可能需要使用复杂的 generator 语法和 yield 关键字。然而,这种方法相对繁琐且容易出错。相比之下,MobX 6 中的 runInAction 提供了一个更简洁、更直观的方式来执行原子性的状态更改。
下面是一个使用 MobX 5 的 generator 语法的示例:
import { observable, action, runInTransaction } from 'mobx'; // MobX 5 导入方式可能有所不同
const store = observable({
count1: 0,
count2: 0,
*incrementBoth() { // 使用 generator 语法定义动作
yield this.count1++; // 使用 yield 来确保动作的原子性
yield this.count2++; // 同上
}
});
runInTransaction(() => { // 使用 runInTransaction 来执行 generator 动作
store.incrementBoth().next(); // 需要手动调用 next() 来执行每一步,较为繁琐
store.incrementBoth().next(); // 同上,容易出错或遗漏步骤
});
相比之下,使用 MobX 6 的 runInAction 语法更加简洁和直观:
import { observable, runInAction } from 'mobx'; // MobX 6 导入方式更简洁统一
const store = observable({
count1: 0,
count2: 0,
});
runInAction(() => { // 使用 runInAction 来确保动作的原子性,无需 generator 语法和 yield 关键字
store.count1++; // 直接在 runInAction 块中执行状态更改即可
store.count2++; // 同上,更加简洁直观且不易出错遗漏步骤。
});
在 React 中使用这三个新增关键功能
下面我将通过一个简单的 React 项目示例来说明如何在 React 中使用这些 API。
设置项目
首先,确保你的项目中已经安装了 React、MobX 和 MobX React。你可以使用以下命令来安装它们(如果尚未安装):
npm install react mobx mobx-react
创建一个简单的 MobX Store
import { observable, autoRun, reaction, runInAction, makeAutoObservable } from 'mobx';
class TodoStore {
todos = observable([
{ id: 1, text: 'Buy milk', completed: false },
{ id: 2, text: 'Go to the gym', completed: false },
{ id: 3, text: 'Write article', completed: false },
]);
constructor() {
makeAutoObservable(this);
// 使用 autoRun 自动跟踪 todos 的变化,并打印信息
autoRun(() => {
console.log('Todos have changed:', this.todos.map(todo => todo.text));
});
// 使用 reaction 对 todos 的数量进行反应
reaction(
() => this.todos.length,
(length, reaction) => {
console.log(`Todo count is now: ${length}`);
}
);
}
addTodo(text) {
runInAction(() => {
this.todos.push({
id: Math.random(),
text: text,
completed: false
});
});
}
toggleTodo(todo) {
runInAction(() => {
todo.completed = !todo.completed;
});
}
}
export default new TodoStore();
在上面的代码中,我们创建了一个 TodoStore 类,该类具有可观察的 todos 数组。我们在构造函数中使用 autoRun 来自动跟踪 todos 数组的变化,并且每当数组发生变化时都会在控制台打印信息。我们还使用 reaction 来跟踪 todos 数组的长度,并在长度变化时在控制台中打印新的长度。
addTodo 和 toggleTodo 方法都使用了 runInAction 来确保添加或切换待办事项的状态变化是原子的,并且在这过程中不会触发任何观察者,直到整个动作完成。
在 React 组件中使用 MobX Store
import React from 'react';
import { observer } from 'mobx-react';
import todoStore from './TodoStore';
const TodoList = observer(() => {
return (
<div>
<h2>Todo List</h2>
<ul>
{todoStore.todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => todoStore.toggleTodo(todo)}
/>
{todo.text}
</li>
))}
</ul>
<button onClick={() => todoStore.addTodo('New Task')}>Add Todo</button>
</div>
);
});
export default TodoList;
在上面的 React 组件中,我们使用了 observer 高阶组件来让组件能够响应 MobX store 中的状态变化。每当 todos 数组或其元素发生变化时,TodoList 组件都会自动重新渲染。
通过点击复选框来切换待办事项的完成状态,或者点击 "Add Todo" 按钮来添加新的待办事项,这些操作都会触发我们在 TodoStore 中定义的 autoRun 和 reaction,从而在控制台中看到相应的输出。
这样,我们就成功地在 React 项目中集成了 MobX,并通过 autoRun、reaction 和 runInAction 这些 API 实现了状态的高效管理。