十分钟,带你了解MobX 与 React

MoBX是一种简单的、可扩展的经过测试的状态管理方案。

虽然说mobx是一个独立的库,但是大多数人都在React中使用它,本教程将在10分钟内带你了解react与mobx的交互式。

核心概念

     状态是每个应用程序的核心,同时也是导致应用程序BUG的重灾区,尤其是当维护的状态面对异步操作时产生不同步的情况时。为此,很多状态管理方案的解决方式是试图通过限制状态的修改方式来控制状态同步,例如通过使状态不可变 immutable。但是这使得数据的标准化与引用完整性不再得到保证,并且也不可能使用像原型这样的强大概念。

    MobX通过解决根本问题使状态管理再次变得简单:它使得不可能产生不一致的状态。实现这一点的策略很简单:确保从应用程序状态派生的所有内容都将自动地派生,保障状态的同步性。

概念上,MUBX像电子表格一样对待你的应用程序。

首先,application state。构成应用程序模型的 Graphs of objects, arrays, primitives, references。这些值是应用程序的“数据单元”。
其次有 derivations。基本上,任何可以从应用程序状态自动计算的值。这些推导或计算值的范围可以从简单的值(如未完成的待办事项的数量)到复杂的东西(如todos的可视化HTML表示)。在电子表格术语中:这些是您的应用程序的公式和图表。
Reactions 与derivations非常相似。主要区别在于这些函数不产生值。相反,它们会自动运行以执行某些任务。通常这与I / O相关。它们确保更新DOM或在适当的时间自动生成网络请求。
最后还有action。行动是改变状态的所有事情。 MobX将确保由您的操作引起的对应用程序状态的所有更改都由所有派生和反应自动处理。同步无故障。

一个简单的todo store......

 下面是一个非常简单的TodoStore,它维护着一系列待办事项。 尚未涉及MobX。

class TodoStore {
    todos = [];

    get completedTodosCount() {
        return this.todos.filter(
            todo => todo.completed === true
        ).length;
    }

    report() {
        if (this.todos.length === 0)
            return "<none>";
        return `Next todo: "${this.todos[0].task}". ` +
            `Progress: ${this.completedTodosCount}/${this.todos.length}`;
    }

    addTodo(task) {
        this.todos.push({
            task: task,
            completed: false,
            assignee: null
        });
    }
}

const todoStore = new TodoStore();

我们刚刚创建了一个带有todos集合的todoStore实例。 是时候用一些对象填充todoStore了。 为了确保我们看到更改的效果,我们在每次更改后调用todoStore.report并记录它。 请注意,报告有意始终仅打印第一个任务。 它使这个例子有点人为,但正如你将在下面看到的,它很好地证明了MobX的依赖性跟踪是动态的。

todoStore.addTodo("read MobX tutorial");
console.log(todoStore.report());

todoStore.addTodo("try MobX");
console.log(todoStore.report());

todoStore.todos[0].completed = true;
console.log(todoStore.report());

todoStore.todos[1].task = "try MobX in own project";
console.log(todoStore.report());

todoStore.todos[0].task = "grok MobX tutorial";
console.log(todoStore.report());

到目前为止,这段代码并没有什么特别之处。 但是,如果我们不必明确地调用report,但是可以声明我们希望在每次状态更改时调用它,该怎么办? 这将使我们免于从代码库中可能影响报告的任何地方调用报告的责任。

幸运的是,这正是MobX可以为您做的。 自动执行完全取决于状态的代码。 这样我们的报告功能就会自动更新,就像电子表格中的图表一样。 为了实现这一点,TodoStore必须变得可观察,以便MobX可以跟踪正在进行的所有更改。 让我们改变课程就足以实现这一目标。

此外,completedTodosCount属性可以从待办事项列表中自动导出。 通过使用@observable和@computed装饰器,我们可以在对象上引入可观察的属性:

class ObservableTodoStore {
@observable todos = [];
@observable pendingRequests = 0;

constructor() {
mobx.autorun(() => console.log(this.report));
}

@computed get completedTodosCount() {
 return this.todos.filter(
todo => todo.completed === true
).length;
}

@computed get report() {
if (this.todos.length === 0)
return "<none>";
return `Next todo: "${this.todos[0].task}". ` +
`Progress: ${this.completedTodosCount}/${this.todos.length}`;
}

addTodo(task) {
this.todos.push({
task: task,
completed: false,
assignee: null
});
}
}


const observableTodoStore = new ObservableTodoStore();

 我们将一些属性标记为@observable,以向MobX发出这些值随时间变化的信号。 计算用@computed进行修饰,以识别这些可以从状态派生。

目前尚未使用pendingRequests和assignee属性,但稍后将在本教程中使用。 为简洁起见,本页面上的所有示例都使用ES6,JSX和装饰器。 但不要担心,MobX中的所有装饰器都有ES5对应物。

在构造函数中,我们创建了一个小函数,它打印报告并将其包装在autorun中。autorun会创建一次运行的响应,然后在函数内部使用的任何可观察数据发生更改时自动重新运行。 由于report使用可观察的待办事项属性,因此它将在适当时打印报告。 

observableTodoStore.addTodo("read MobX tutorial");
observableTodoStore.addTodo("try MobX");
observableTodoStore.todos[0].completed = true;
observableTodoStore.todos[1].task = "try MobX in own project";
observableTodoStore.todos[0].task = "grok MobX tutorial";

 报告自动同步打印,不会泄漏中间值。 如果仔细调查日志,您将看到第四行没有产生新的日志行。 因为重命名后报告实际上没有改变,尽管支持数据确实如此。 另一方面,更改第一个待办事项的名称会更新报告,因为该名称在报告中有效使用。 这很好地证明了自动运行不仅可以观察到todos数组,还可以看到todo项目中的各个属性。

Making React reactive

是时候围绕这个同一个商店建立一个被动的用户界面了。 React组件没有开箱即用的反应。 mobx-react软件包中的@observer装饰器通过将React组件呈现方法包装在自动运行中来自动修复,从而自动保持组件与状态同步。

下一个清单定义了一些React组件。那里唯一的MobX就是@observer装饰器。这足以确保每个组件在相关数据更改时单独重新呈现。您不再需要调用setState,也不必使用需要配置的选择器或更高阶组件来了解如何订阅应用程序状态的正确部分。基本上,所有组件都变得聪明。然而,它们是以愚蠢的,陈述性的方式定义的。

@observer
class TodoList extends React.Component {
render() {
const store = this.props.store;
return (
<div>
{ store.report }
<ul>
{ store.todos.map(
(todo, idx) => <TodoView todo={ todo } key={ idx } />
) }
</ul>
{ store.pendingRequests > 0 ? <marquee>Loading...</marquee> : null }
<button onClick={ this.onNewTodo }>New Todo</button>
<small> (double-click a todo to edit)</small>
<RenderCounter />
</div>
);
}

onNewTodo = () => {
this.props.store.addTodo(prompt('Enter a new todo:','coffee plz'));
}
}

@observer
class TodoView extends React.Component {
render() {
const todo = this.props.todo;
return (
<li onDoubleClick={ this.onRename }>
<input
type='checkbox'
checked={ todo.completed }
onChange={ this.onToggleCompleted }
/>
{ todo.task }
{ todo.assignee
? <small>{ todo.assignee.name }</small>
: null
}
<RenderCounter />
</li>
);
}

onToggleCompleted = () => {
const todo = this.props.todo;
todo.completed = !todo.completed;
}

onRename = () => {
const todo = this.props.todo;
todo.task = prompt('Task name', todo.task) || todo.task;
}
}

ReactDOM.render(
<TodoList store={ observableTodoStore } />,
document.getElementById('reactjs-app')
);

下一个清单很好地表明我们只需改变我们的数据而不做任何其他事情。 MobX将从商店中的状态再次自动派生和更新用户界面的相关部分。

const store = observableTodoStore;
store.todos[0].completed = !store.todos[0].completed;
store.todos[1].task = "Random todo " + Math.random();
store.todos.push({ task: "Find a fine cheese", completed: true });
// etc etc.. add your own statements here...

使用references

到目前为止,我们已经创建了可观察对象(原型对象和普通对象),数组和基元。 您可能想知道,MobX中的引用是如何处理的? 我的state是否允许形成图表?
  在之前的清单中,您可能已经注意到todos上有一个assignee属性。 下面我们给它赋值并引入另一个“商店”,包括人和给人分配的任务。

var peopleStore = mobx.observable([
    { name: "Michel" },
    { name: "Me" }
]);
observableTodoStore.todos[0].assignee = peopleStore[0];
observableTodoStore.todos[1].assignee = peopleStore[1];
peopleStore[0].name = "Michel Weststrate";
我们现在有两个独立的商店。 一个人和一个待办事项。 要将assignee从人员商店分配给某个人,我们只是分配了一个参考。 这些更改将由TodoView自动获取。 
使用MobX,不需要首先规范化数据并编写选择器以确保我们的组件将被更新。 事实上,数据的存储位置甚至都不重要。 只要对象可观察,MobX就能跟踪它们。
真正的JavaScript引用将起作用。 如果MobX与派生相关,它们将自动跟踪它们。

异步操作

由于我们的小型Todo应用程序中的所有内容都来自状态,因此状态发生变化时无关紧要。 这使得创建异步操作非常简单。 只需按下以下按钮(多次)即可模拟异步加载新的待办事项:

这背后的代码非常简单。 我们首先更新store属性pendingRequests,让UI反映当前的加载状态。 加载完成后,我们更新商店的待办事项并再次减少pendingRequests计数器。 只需将此代码段与早期的TodoList定义进行比较,即可了解pendingRequests属性的使用方式。

observableTodoStore.pendingRequests++;
setTimeout(function() {
    observableTodoStore.addTodo('Random Todo ' + Math.random());
    observableTodoStore.pendingRequests--;
}, 2000);

DevTools

mobx-react-devtools软件包可以在任何MobX + ReactJS应用程序中使用。 单击第一个按钮将突出显示正在重新呈现的每个@observer组件。 如果单击第二个按钮并在预览中的其中一个组件之后,将显示该组件的依赖关系树,以便您可以在任何给定时刻精确检查它正在观察哪些数据。

结论

就这样!没有样板。只是一些简单的声明性组件,构成了我们完整的UI。它们完全来自我们的状态。您现在可以开始在自己的应用程序中使用mobx和mobx-react软件包了。到目前为止您学到的东西的简短摘要:

使用@observable装饰器或可观察(对象或数组)函数使MobX可以跟踪对象。
@computed装饰器可用于创建可以从状态自动派生其值的函数。
使用autorun自动运行依赖于某些可观察状态的函数。这对于记录,发出网络请求等非常有用。
使用mobx-react包中的@observer装饰器使您的React组件真正具有反应性。它们将自动高效地更新。即使在具有大量数据的大型复杂应用程序中使用。
使用上面的可编辑代码块随意玩一下,可以基本了解MobX如何对所有更改做出反应。例如,您可以向报表函数添加一个日志语句,以查看它何时被调用。或者根本不显示报告,看看它如何影响TodoList的渲染。或仅在特定情况下显示...

MobX不是状态容器

人们经常使用MobX作为Redux的替代品。 但请注意,MobX只是一个解决技术问题的库,而不是一个架构甚至是状态容器本身。 从这个意义上说,上面的例子是人为设计的,建议使用适当的工程实践,比如在方法中封装逻辑,在商店或控制器中组织它们。或者,就像HackerNews上的某个人所说:

“MobX,它已在其他地方被提及,但我不禁赞不绝口。 在MobX中编写意味着使用控制器/调度程序/操作/主管或管理数据流的另一种形式回归到架构问题,您可以根据应用程序的需要进行模式化,而不是默认情况下需要的东西,而不仅仅是Todo应用程序。“

翻译自https://mobx.js.org/getting-started.html

原文地址:https://www.cnblogs.com/ymlblogs/p/10097607.html