MobX 状态管理

前言

MobX 是 Redux 以后的一个状态管理库,它相较于 redux 更轻量,总体是一个观察者模式的架构,存储 state 的 store 是被观察者,使用 store 的组件是观察者。MobX 能够有多个 store 对象,而且 store 使用的 state 是能够改变的。java

概念简介

MobX 经过函数响应式编程的思想使得状态管理变得简单和易于扩展,其背后的哲学是:任何从应用程序的状态中获取/衍生的数据都应该能够自动被获取/衍生。和 Redux 同样,MobX 也是采用单向数据流:经过 action 改变应用的 state ,state 的改变会致使 view 的更新。react

单向数据流

MobX 包含 4 个概念:state(状态)、computed value(计算值),reaction(响应),action(动做)。state 的更新过程是同步执行的,也就是说,action 更改 state 后,新的 state 能够当即被获取。而 computed value 采用的是延迟更新,只有当它被使用的时候才会从新计算值,当使用 computed value 的组件被卸载时,它也会被自动回收。npm

MobX 中提到的 state 指的是能够观测的 state,由于对于不可观测的 state ,他们的修改并不会自动产生影响,对 MobX 的数据流来讲是没有意义的。编程

MobX 中大量使用了 ES.next 的装饰器语法,这也是官方所推荐的,但装饰器语法目前还处于实验阶段,须要安装 babel-plugin-transform-decorators-legacy 这个 babel 插件。redux

简单的 Todos 应用

经过 Todos 应用来简单介绍这 4 个概念。使用 class 定义一个可观测的 state Todo,表明一项任务:数组

import { observable } from "mobx";
class Todo {
    id = Math.random();
    @observable title = "";
    @observable finished = false;
}
复制代码复制代码

这里使用的 @observable 就是装饰器语法,也可使用 ES5 语法实现等价代码:bash

import { extendObservable } from "mobx";
function Todo {
    this.id = Math.random();
    extendObservable(this, {
        title: "",
        finished: false
    });
}
复制代码复制代码

通过 @observable 的修饰以后,Todo 的 title 和 finished 两个属性就变成可观测状态,他们的改变会自动被观察者获知。id 没有被 @observable 修饰,因此它只是一个普通属性。 基于可观测的 state 能够建立 computed value。例如,todos 中须要获取未完成任务的总数,使用 @computed 定义一个 unfinishedTodoCount,计算未完成任务的总数:babel

import { observable, computed } from "mobx";
class TodoList {
    @observable todos = [];
    @computed get unfinishedTodoCount() {
        return this.todos.filter(todo => !todo.finished).length;
    }
}
复制代码复制代码

TodoList的属性 todos 也是一个可观测的属性,数组里存放的是前面定义的 Todo 实例对象。当 todos 中的元素数量发生变化或者其中某个 todo 元素的 finished 属性变化,unfinishedTodoCount 都会自动更新(在使用的时候)。 除了 computed value 会响应 state 的变化,reaction 也会响应 state 的变化,不一样的是,reaction 并非建立一个新值,它是用来执行有反作用的逻辑,好比输出日志、发送请求等。mobx-react 提供了 @observer 装饰器和 observer 函数,能够将 React 组件封装成 reaction,自动根据 state 的变化更新 UI 组件。例如,建立 TodoListView 和 TodoView 两个组件表明应用的 UI:架构

import React, { Component } from "react";
import ReactDOM from "react-dom";
import { oberver } from "mobx-react";
import { action } from "mobx";

@observer
class TodoListView extends Component {
  render() {
    return (
      <div>
        <ul>
          {this.props.todoList.map(todo => {
            <TodoView todo={todo} key={todo.id} />;
          })}
        </ul>
        Tasks left: {this.props.todoList.unfinishedTodoCount}
      </div>
    );
  }
}

const TodoView = oberver(({ todo }) => {
  const handleClick = action(() => todo.finished = !todo.finished)
  return (
    <li>
      <input type="checkbox" checked={todo.finished} onClick={handleClick}/>
      {todo.title}
    </li>
  );
});

const store = new TodoList();
ReactDOM.render(
  <TodoListView todoList={store} />, document.getElementById("root")
);
复制代码复制代码

TodoListView 使用到了可观测的数据:todos 和 todo.finished(经过 unfinishedTodoCount 间接使用的),因此它们的改变会触发 TodoListView 的视图更新。 TodoView 中的 handleClick 是用来改变 todo.finished 的 action,也就是经过 mobX 更改应用的状态,通常习惯使用 mobX 提供的 action 包装应用中定义的 action,dom