React使用Mobx管理数据

React 和 Vue一样都属于单向数据流,为了更好的进行状态和数据管理官方和第三方也有配套的Redux等插件,本文介绍一个个人觉得更易用使用的组件 Mobx

核心概念

MobX 处理你的应用程序状态如下图所示

常用的几个装饰器 (装饰器解释移步)

  • Actions: 改变state的操作。

  • ObservableState:应用的可被观察的数据状态。

  • Computed: 从state中通过纯函数的操作衍生出的值,state变化它也会跟着变化。

  • Reactions:需要对state变化动态作出反应的东西,它包含不同的概念,基于被观察数据的更新导致某个计算值,或者是发送网络请求以及更新视图等,都属于响应的范畴,这也是响应式编程在 JavaScript 中的一个应用。

  • Autorun: 依赖收集,监听触发,autorun 背后由 reaction 实现。由于 autorun 与 view 的 render 函数很像,我们在 render 函数初始化执行时,使其包裹在 autorun 环境中,第 2 次 render 开始遍剥离外层的 autorun,保证只绑定一遍数据。这样 view 层在原本 props 更新机制的基础上,增加了 autorun 的功能,实现修改任何数据自动更新对应 view 的效果。(ps:使用autoRun实现Mobx-react非常简单,核心思想是将组件外面包上autoRun,这样代码中用到的所有属性都会像上面Demo一样,与当前组件绑定,一旦任何值发生了修改,就直接forceUpdate,而且精确命中,效率最高。)

React项目中Mobx的安装

npm或yarn安装插件

npm install mobx-react --save
或者
yarn add mobx-react --save

装饰器的启用,装饰器目前仍处于提案阶段因此需要做特殊处理

修改package.json添加如下引用,这个和npm手动安装一个道理

"@babel/core": "^7.1.0",
    "@babel/plugin-proposal-class-properties": "^7.1.0",
    "@babel/plugin-proposal-decorators": "^7.1.0",
    "@babel/preset-env": "^7.1.0"
使用npm run eject确认显示所有隐藏的配置文件

注意此步骤不可逆

在根目录创建.babelrc文件,添加如下内容
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ],
  "plugins":  [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", { "loose": true }]
  ]
}
删除package.json中babel配置

几个核心装饰器的解释

@observable 创建需要被监听的应用状态

通过对Class的属性简单的使用@observable修饰符,就定义了一个需要被监听的应用状态变量;然后直接在类中定义对应用状态变量的操作;我们就实现了一个灵活的Store层

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import TodoBox from './Welcome';
import {observable, action} from 'mobx';

class Store{

    @observable todos=[{
        key:1,
        title: "todo标题",
        done: false,
    },{
        key:2,
        title: "todo-2",
        done: false,
    }];
}

如上面代码所示,Store是一个store层的类,用来对状态进行管理

@observer 创建应用状态的监听者

有多种方式可以创建应用状态的监听者(Reactions),包括autorun、reaction、@observer等

此处示例显示如何用@observer来监听

import React, {Component} from 'react';
import {observer} from "mobx-react";
import {action} from "mobx";


@observer
class TodoBox extends Component {


    /**
     *
     * @param props
     */
    constructor(props) {
        super(props);
    }


    render() {

        const store=this.props.store;
        return (
            <div>
                <ul>
                    {store.todos.map((todo,_) => <li>{todo.title}</li>)}
                </ul>
              
            </div>
        )
    }
}


export default TodoBox;

@action

可以看到之前的监听模式下,其实完全可以直接通过store层对数据进行读取和写入,虽然很便捷但却打破了单向数据流向的原则,Mobx也考虑到这点所以特意设计了action,并且要求用户尽可能的使用action来设定更改状态和数据的方法

class Store{

    @observable todos=[{
        key:1,
        title: "todo标题",
        done: false,
    },{
        key:2,
        title: "todo-2",
        done: false,
    }];


    @action
    changeTitle(){
        // 直接修改仓库中的状态值
        this.todos[0].title = "修改后的todo标题"
    }
}

完整Demo

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import TodoBox from './Welcome';
import {observable, action} from 'mobx';


class Store{

    @observable todos=[{
        key:1,
        title: "todo标题",
        done: false,
    },{
        key:2,
        title: "todo-2",
        done: false,
    }];


    @action
    changeTitle(){
        // 直接修改仓库中的状态值
        this.todos[0].title = "修改后的todo标题"
    }
}

const store1=new Store();

ReactDOM.render(<TodoBox store={store1} />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

import React, {Component} from 'react';
import {observer} from "mobx-react";
import {action} from "mobx";


@observer
class TodoBox extends Component {


    /**
     *
     * @param props
     */
    constructor(props) {
        super(props);
    }




    render() {

        const store=this.props.store;
        return (
            <div>
                <ul>
                    {store.todos.map((todo,_) => <li>{todo.title}</li>)}
                </ul>
                <div>
                    <input type="button" onClick={() => {

                        this.props.store.changeTitle();
                        // // 直接修改仓库中的状态值
                        // this.props.store.todos[0].title = "修改后的todo标题"
                    }} value="点我"/>
                </div>

            </div>
        )
    }
}


export default TodoBox;
posted @ 2019-01-29 15:00  linkanyway  阅读(2510)  评论(0编辑  收藏  举报