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;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM