前言

目前React框架越来越火,使用 react 公司越来越多!随着页面逻辑的复杂度提升,各种状态库也层次不穷,业内比较成熟的解决方案有 Redux、但是Redux使用相对繁琐,对于新手来说不友好。所以今天特意推荐大家可以去尝试使用另一个比较成熟的状态管理工具Mobx,mobx 的核心理念是 简单、可扩展的状态管理库。

个人感觉它和vuex有很多相似之处,如果你会vuex十分钟就可以搞定。

Mobx它通过透明的函数响应式编程(transparently applying functional reactive programming - TFRP)使得状态管理变得简单和可扩展。

准备工作

可以使用create-react-app构建一个react项目,也可以看我之前写的webapck小白成神之路文章手动搭建一套react项目(个人推荐第二种,手动搭建会学到很多知识点)

环境配置

安装mobx mobx-react

  npm install --save mobx mobx-react

因为要用到装饰器,所以需要安装一个依赖去解析

  npm i --save-dev babel-plugin-transform-decorators-legacy

然后在.babelrc配置

{
  "presets": ["react", "env", "stage-0"], 
  "plugins": ["transform-runtime", "transform-decorators-legacy"] 
}

observable

仓库

新建test.js

import {observable} from 'mobx';

class TestStore {
    // 被观察者
    @observable name; 
    constructor() {
        this.name = '浮云先生'
    }
}
const test = new TestStore() 
export default test

可以看到我们在数据仓库中写入name = "浮云先生",

observer

组件

import React from 'react'
import ReactDOM from 'react-dom'
import {observer, inject} from 'mobx-react';

// 观察者
@inject('test') 
@observer
class DemoComponent extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        const { test } = this.props;
        return (
            <div>
                <p>{test.name}</p>
            </div>
        );
    }
}

export default DemoComponent;

  • inject 在模块内用 @inject('Store'),将 Store 注入到 props 上,保证结构的一致性
    使用 @observer ,将组件变为观察者,响应 name 状态变化。
    当状态变化时,组件也会做相应的更新。

computed

有时候,state 并不一定是我们需要的最终数据。例如,所有的 todo 都放在 store.todos 中,而已经完成的 todos 的值(store.unfinishedTodos),可以由 store.todos 衍生而来。

对此,mobx 提供了 computed 装饰器,用于获取由基础 state 衍生出来的值。如果基础值没有变,获取衍生值时就会走缓存,这样就不会引起虚拟 DOM 的重新渲染。

通过 @computed + getter 函数来定义衍生值(computed values)。

import { computed } from 'mobx';

class Store {
  @observable todos = [{
    title: "todo标题",
    done: false,
  },{
    title: "已经完成 todo 的标题",
    done: true,
  }];

  @computed get finishedTodos () {
    return  this.todos.filter((todo) => todo.done)
  }
}

这样组建中就可以直接拿到finishedTodos 过滤后的值了

action

首先在 Store 中,定义action(动作)(test.js)

import {observable, action} from 'mobx';

class TestStore {
    @observable name; 
    // 定义action(动作)
    @action 
    changeName = name => {
        this.name = name
    }

    constructor() {
        this.name = '浮云先生'
    }
}
const test = new TestStore() 
export default test

在组件中只需要this.props.test.changeName ('改变')调用就可以。是不是很简单~

异步请求

方式1:runInAction/async/await

import {observable, action} from 'mobx';
class MyState {
    @observable data = null;
    @observable state = null;
    @action initData = async () => {
        try {
            const data = await getData("xxx");
            runInAction("说明一下这个action是干什么的。不写也可以", () => {
                this.state = "success"
                this.data = data;
            })
        } catch (error) {
            runInAction(() => {
                this.state = "error"
            })
        }
        
    };
}

方式2:flows

更好的方式是使用 flow 的内置概念。它们使用生成器。一开始可能看起来很不适应,但它的工作原理与 async / await 是一样的。只是使用 function * 来代替 async,使用 yield 代替 await 。 使用 flow 的优点是它在语法上基本与 async / await 是相同的 (只是关键字不同),并且不需要手动用 @action 来包装异步代码,这样代码更简洁。

class Store {
    @observable data = null;
    @observable state = null;

    fetchProjects = flow(function * () { // <- 注意*号,这是生成器函数!
        try {
            const projects = yield getData() // 用 yield 代替 await
            // 异步代码块会被自动包装成动作并修改状态
            this.state = "success"
            this.data = projects
        } catch (error) {
            this.state = "error"
        }
    })
}

大型项目中定义多个状态(模块化)

假如我们有两个模块分别是test.js和mast.js
test.js

import {observable, action} from 'mobx';

class TestStore {
    @observable name; 
    @observable age;

    @action 
    changeAge = i => {
        this.age = this.age + Number(i)
    }

    constructor() {
        this.name = '浮云先生'
        this.age = 25
    }
}
const test = new TestStore() 
export default test

mast.js

// 这里引入的是 mobx
import {observable, computed, action} from 'mobx';

class MastSotre {
    @observable list; 

    @computed
    get getList () {
        return this.list.filter(v => v.id !== 1)
    }

    @action
    addList = obj => this.list.push(obj)

    constructor() {
        this.list = [
            {
                name: '香蕉',
                id: 0
            },
            {
                name: '苹果',
                id: 1
            },
            {
                name: '西瓜',
                id: 2
            }
        ]
    }
}
const mast = new MastSotre() 
export default mast

新建index.js进行汇总

// 汇总store
import test from './test'
import mast from './mast'

const stores = {
    test,
    mast,
}
export default stores

在入口文件中全局注入数据

import React from 'react'  
import ReactDOM from 'react-dom'  
import RouterContainer from './router/index'
import { Provider } from "mobx-react"
import stores from './store'

import {configure} from 'mobx'; // 开启严格模式
configure({enforceActions: true}) // 开启严格模式

ReactDOM.render(
    <Provider {...stores}>
        <RouterContainer/>
    </Provider>,
    document.getElementById('example')
);
  • Provider 通过 Provider 渗透
  • configure 代表开启了严格模式,因为非严格模式下,组件是直接可以通过props.action改变state数据的,当项目复杂的时候这样是非常危险的。所以要设置唯一改变state方式是通过action

简单的对常用的知识点进行了汇总,同学们看了后是不是觉得很简单!
想要深入学习的Mobx官网传送门:https://cn.mobx.js.org/



作者:抱紧我_8204
链接:https://www.jianshu.com/p/6a12b3121379
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

Logo

前往低代码交流专区

更多推荐