您的位置:首页 > Web前端 > React

[置顶] React 项目中使用 MobX 进行状态管理

2018-02-24 09:54 806 查看

React 项目中使用 MobX 进行状态管理

说明

MobX 是一个简单的、可扩展的状态管理器,他通过透明的函数式编程使得状态管理变的简单和可扩展

React 和 MobX 是一对强力组合。React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染。而 MobX 提供机制来存储和更新应用状态供 React 使用。

参考 MobX 中文文档

完整项目代码

概念

State 状态

Derivations 衍生,源自状态,并且会不再有进一步相互作用的东西

Computed values 计算值,使用纯函数从当前可观察状态中衍生出的值

Reactions 反应,当状态改变时,需要自动发生的副作用

Actions 动作,任意一段可以改变状态的代码,尽量只在动作中修改状态

原则

MobX 中支持单项数据流,也就是动作改变状态,而状态的改变会更新所有受影响的视图

状态改变时,所有衍生都会进行原子级的自动更新,因此不会观察到中间值

所有衍生默认都是同步更新,这意味着可以在改变状态之后更安全的检查计算值

计算值是延迟更新的,任何不在使用状态的计算值将不会更新

所有计算值都应该是纯净的,他们不应该用来改变状态

API

@observable
一个装饰器,用于将对象的某一部分变成可观察的

@computed
一个装饰器,计算值,根据现有状态或其他计算值衍生出的值

@computed.struct
用于比较结构,设置为 true 时,表达式的输出在结果上与先前的值进行比较,然后通知观察者相关的更改

@computed.equals(comparer)
用于自定义比较结构,comparer 是一个自定义比较函数,mobx 提供三个内置比较器 同过
import { comparer } from 'mobx'
得到
comparer.default


Autorun
当需要创建一个响应式函数,但是函数本省并不需要观察者时,可以使用此 api,与
computed
相同点是都会响应依赖的变化,不同点是
computed
会返回一个新的值,用作
de22
观察者观察,而
autorun
没有观察者,他只是响应变化后执行一段代码

@observer
mobx-react
提供的装饰器,用来将 react 组件转换为响应式的组件,需要确保
observer
是最深处(第一个应用)

Provider
组件,由
mobx-react
提供,它使用了 react 的上下文(context)机制,可以用来向下传递
stores


@inject()
将组件链接到
stores
,需要传递一个 stores 名称的列表给
inject
使得 stores 可以作为组件的 props 使用

componentWillReact
声明周期钩子,当组件因为它观察的数据发生变化,他会被安排重新渲染,这个时候
componentWillReact
钩子会被触发

action
动作,动作是用来修改状态的

完整案例代码

1.
/index.js
入口文件

/*
项目入口
*/
import './scss/index.scss';

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { useStrict } from 'mobx';
import { Provider } from 'mobx-react';
import Root from './router/routes';
import stores from './store/index';

const mountNode = document.getElementById('app');

useStrict(true);

const render = (Component, stores) => {
// 卸载掉旧的组件
ReactDOM.unmountComponentAtNode(mountNode);
// 重新渲染新的组件
ReactDOM.render(
<Provider {...stores}>
<BrowserRouter>
<Component />
</BrowserRouter>
</Provider>,
mountNode
);
};

render(Root, stores);

/*
这里的热更新只监视 store 部分代码变化,然后进行重新渲染组件,
组件的热更新还是交给 react-hot-loader 组件处理(route文件中)
*/

if (module.hot) {
module.hot.accept('./store/index.js', () => {
console.log('mobx changed');
render(
require('./router/routes.js').default,
require('./store/index.js').default
);
});
}


2.
/store/index.js
store 状态集合

/*
合并后的 store
*/
import app from './modules/app';
import member from './modules/member';

const stores = {
app,
member
};

export default stores;


3.
/store/modules/members.js
每一个页面的子 store

/*
成员列表页
*/

import { observable, computed, action } from 'mobx';
// 将获取数据部分分离出去
import {
obtainMemberList,
postNewMember,
deleteMember
} from '../../api/members';

class MemberStore {
// 将需要观察的属性设置为可观察的
@observable members;
@observable filter;

// 在这里给可观察的属性设置初始值
constructor() {
this.members = [];
this.filter = '';
}

// 计算属性
@computed
get filtedMembers() {
const members = [...this.members];
if (this.filter === '') {
return members;
}

const filterReg = new RegExp(this.filter, 'g');

return members.filter(
({ name, tel }) => filterReg.test(name) || filterReg.test(tel)
);
}

// 动作,代码专注于更新可观察属性,额外的操作分离出去
@action
changeMembers = members => {
this.members = members;
};
@action
changeFilter = newFilter => {
this.filter = newFilter;
};

/*
一些函数,包含更新可观察属性的部分已经被分离为 action
在 action 中使用异步函数或者 promise 都比较麻烦,所以尽可能的分离,
据文档指出,不但 异步函数需要被 @action
await 后的代码如果更改了可观察属性,需要使用 runInAction 包裹
*/

getMembers = async () => {
const members = await obtainMemberList();

this.changeMembers(members);
};

postMember = async newMember => {
await postNewMember(newMember);
await this.getMembers();
};

deleteMember = async memberId => {
await deleteMember(memberId);
await this.getMembers();
};
}

// 返回一个实例
export default new MemberStore();


4.
/container/Member.js
容器组件

/*
成员列表
*/

import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';
import MemberCom from '../components/member/index';

@inject('member') // 给组件注入其需要的 store,指定对应的子 store 名称
@observer // 将组件转化为响应式组件
export default class Member extends Component {
constructor(props) {
super(props);
}
render() {
const { member } = this.props;

return <MemberCom member={member} />;
}
}


5.
/components/member/index.js
展示组件

/*
MemberCom
*/
import React, { Component } from 'react';
import { observer } from 'mobx-react';
import Toolbar from './Toolbar';
import MemberList from './MemberList';

import './member.scss';

/*
这里有个坑,只要子组件使用了 store 中的可观察属性,
就需要通过 @observer 将其变成响应式组件
*/

@observer
export default class MemberCom extends Component {
constructor(props) {
super(props);
}
render() {
const {
filtedMembers,
filter,
postMember,
deleteMember,
changeFilter
} = this.props.member;

return (
<div id="member-container">
<Toolbar
filter={filter}
postMember={postMember}
changeFilter={changeFilter}
/>
<MemberList
filtedMembers={filtedMembers}
deleteMember={deleteMember}
/>
</div>
);
}
componentWillMount() {
const { getMembers } = this.props.member;
getMembers(); // 请求获取初始数据
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: