react mobx状态管理 基本使用

参考链接:

juejin.cn/post/684490…

Mobx官方文档

前言

Mobx是一个透明函数响应式编程(Transparently Functional Reactive Programming,TFRP)的状态管理库,它使得状态管理简单可伸缩:

1. 基本概念

在这里插入图片描述

  • actions:一些改变状态值(state)的动作。
  • state:可观察的状态值
  • computed value:根据state,用pure function计算出来的值
  • reactions:因state或computed value变化而引起的反应,主要指视图UI重新渲染

2. 基本使用

安装:

npm install mobx --save
npm install mobx-react --save
复制代码

创建一个mobx store的实例:

import {observable, computed, action} from "mobx";

 class Store{
    @observable cities = [];

    @computed
    get total() {
        return this.cities.length;
    }

    @action.bound
    clearCities() {

        this.cities = [];

    }

    @action.bound
    add(values) {
        this.cities = values;
    }
}
export new Store()
复制代码
  • @obserbale 表示定义一个state值
  • @computed 计算属性,表示根据现有的数据计算出来的衍生值,将函数当作属性(函数的return值)用
  • @action.bound@aciton:表示定义更新state的方法,里面是更新state的逻辑,通过这种方式可以直接通过this.xxx来修改state

2.1 含异步操作的action

mobx不允许在@action中的Promise.then或async @aciton等异步操作中直接进行state的数据更新操作

官方解释:

action 包装/装饰器只会对当前运行的函数作出反应,而不会对当前运行函数所调用的函数(不包含在当前函数之内)作出反应! 这意味着如果 action 中存在 setTimeout、promise 的 then 或 async 语句,并且在回调函数中某些状态改变了,那么这些回调函数也应该包装在 action 中。

一种可行的做法是针对Promise的then回调中修改state的部分也封装成一个@aciton,则调用该aciton而不是在回调中直接修改则可以解决该问题

推荐的做法是使用mobx提供的runInAction来修改state:

在这里插入图片描述

Promise.then中:

class Store {
    @observable githubProjects = []
    @observable state = "pending" // "pending" / "done" / "error"

    @action
    fetchProjects() {
        this.githubProjects = []
        this.state = "pending"
        fetchGithubProjectsSomehow().then(
            projects => {
                const filteredProjects = somePreprocessing(projects)
                // 将‘“最终的”修改放入一个异步动作中
                runInAction(() => {
                    this.githubProjects = filteredProjects
                    this.state = "done"
                })
            },
            error => {
                // 过程的另一个结局:...
                runInAction(() => {
                    this.state = "error"
                })
            }
        )
    }
}
复制代码

async的action中:

 @action
    async fetchProjects() {
        this.githubProjects = []
        this.state = "pending"
        try {
            const projects = await fetchGithubProjectsSomehow()
            const filteredProjects = somePreprocessing(projects)
            // await 之后,再次修改状态需要动作:
            runInAction(() => {
                this.state = "done"
                this.githubProjects = filteredProjects
            })
        } catch (error) {
            runInAction(() => {
                this.state = "error"
            })
        }
    }
复制代码

2.2 @action.bound和@aciton的区别

官方解释:

action 装饰器/函数遵循 javascript 中标准的绑定规则。 但是,action.bound 可以用来自动地将动作绑定到目标对象(即当前Store的实例)。

注意: action.bound 不要和箭头函数一起使用;箭头函数已经是绑定过的并且不能重新绑定。

示例如下:

在这里插入图片描述

在这里插入图片描述

2.3 页面使用mobx

在需要的页面中,引入对应Store的实例,并使用mobx-react的@observer进行数据监听,以实现数据的展示以及数据更新来驱动试图的更新

Store.js

import {observable, computed, action} from "mobx";

 class Store{
    @observable cities = ["北京","上海"];
     @observable name = "scw";

    @computed
    get total() {
        return this.cities.length;
    }

    @action.bound
    clearCities() {
        this.cities = [];

    }

    @action.bound
    add(values) {
        this.cities = values;
    }
}
export new Store()
复制代码

页面组件:

import { observer, inject } from "mobx-react"
import Store from "./Store"

@observer
class Page extends Component {
  componentDidMount() {
    Store.add("南京")
  }

  render() {
    return (
        <div>
          {Store.name}
          {Store.ciies.map(item=><span>{item}</span>)}
        </div>
    )
  }
}

export default Page
复制代码

2.3 mobx的全局注入

mobx不会强制像redux那样必须全局注入store,如2.2所示,在mobx中,我们可以针对每个特定的页面或者功能编写专有的store来使用。

不过它也可以在全局注入单个或多个Store来供全局页面组件使用。一般用于一些公共状态或公共方法的设置

所有store的统一管理文件:

你可以在这里引入一些公共的Store,统一封装成对象

import City from "./city";

export default {
    city: new City()
};
复制代码

顶层组件:

这里引入mobx-react的Providr进行公共Store的全局注入:

import React from "react";
import {render} from "react-dom";
import {Provider} from "mobx-react";
import {Router, browserHistory, hashHistory} from "react-router";
import { RouterStore, syncHistoryWithStore } from "mobx-react-router";
import routes from "./routes";
import store from "./mobstore";

const routingStore = new RouterStore();
const history = syncHistoryWithStore(hashHistory, routingStore);

store.routing = routingStore;

render(
    <Provider {...store}>
        <Router history={history} routes={routes}/>
    </Provider>,
    document.querySelector(".container")
);
复制代码

组件使用全局注入的Store:

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



@inject("city") 
@observer
export default class CitySelector extends Component {
    constructor(...props) {
        super(...props);
        this.state = {
            city: []
        };
    }
   onChange = (value) => {

        this.props.city.add(value);
    };

    clearCities() {

        this.props.city.clearCities();

    }

    render() {

        let { cities, total } = this.props.city;
    ....
复制代码

使用@inject("xxx")来调用公共Store对象中指定属性名的Store,此时该组件可通过this.props.xxx来访问对应的Store。包括里面的数据和方法即@observable和@aciton

3. Redux vs Mobx

(1)单一store和多store

store是应用管理数据的地方,在Redux应用中,我们总是将所有共享的应用数据集中在一个大的store中,

而Mobx则通常按模块将应用状态划分,在多个独立的store中管理。

(2)JavaScript对象和可观察对象

Redux默认以JavaScript原生对象形式存储数据,而Mobx使用可观察对象:

  • Redux需要手动追踪所有状态对象的变更;
  • Mobx中可以监听可观察对象,当其变更时将自动触发监听;

(3)不可变(Immutable)和可变(Mutable)

Redux状态对象通常是不可变的(Immutable),我们不能直接操作状态对象,而总是在原来状态对象基础上返回一个新的状态对象,这样就能很方便的返回应用上一状态;

而Mobx中可以直接使用新值更新状态对象。

(4)mobx-react和react-redux

使用Redux和React应用连接时,需要使用react-redux提供的Provider和connect:

  • Provider:负责将Store注入React应用;
  • connect:负责将store state注入容器组件,并选择特定状态作为容器组件props传递;

对于Mobx而言,同样需要两个步骤:

  • Provider:使用mobx-react提供的Provider将所有stores注入应用;
  • 使用inject将特定store注入某组件,store可以传递状态或action;然后使用observer保证组件能响应store中的可观察对象(observable)变更,即store更新,组件视图响应式更新。

选择Mobx的原因

学习成本少:Mobx基础知识和配置很简单,而Redux确较繁琐,流程较多,需要配置,创建store,编写reducer,action,如果涉及异步任务,还需要引入redux-thunk或redux-saga编写额外代码,Mobx流程相比就简单很多,并且不需要额外异步处理库;

不选择Mobx的原因

过于自由:Mobx提供的约定及模版代码很少,这导致开发代码编写很自由,如果不做一些约定,比较容易导致团队代码风格不统一,所以当团队成员较多时,确实需要添加一些约定;也正因为这样,Mobx可拓展,可维护性难度较高

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享