copy from : https://blog.csdn.net/smk108/article/details/85053903

Mobx提供了一个mobx-react包帮助开发者方便地在React中使用Mobx,mobx-react中有observer、Provider、inject几个常用的api。在《mobx系列(二)-mobx主要概念》中我们已经介绍过observer,本文介绍下inject、Provider,以及Mobx如何与React结合使用。

1、Provider

Provider是一个React组件,使用React的上下文(context)机制,可以用来向下传递stores,即把state传递给其子组件。

例如,有如下形式的一个store:

import {observable, computed, action} from 'mobx';
class userStoreClass {
@observable user = {
name: 'admin',
role: '管理员'
};
count = 0;
@computed get userName(){
return this.user.name;
}
@action changeUser(){
if(this.count % 2 === 1){
this.user = {
name: 'admin',
role: '管理员'
};
}else{
this.user.name = 'guest';
this.user.role = '访客';
this.user.isGuest = 'true';
}
this.count ++;
}
}
const userStore = new userStoreClass();
export default userStore;

使用Provider传递store的方式为:

import React from 'react';
import ReactDOM from 'react-dom';
import {configure} from 'mobx';
import {Provider} from 'mobx-react';
import userStore from './models/userStore';
import App from './components/App';
// 状态始终需要通过动作来更新(实际上还包括创建)
configure({'enforceActions': 'always'});
ReactDOM.render((
<Provider store={userStore}}>
<App />
</Provider>
), document.getElementById('container'));

如果有多个store,可以使用类似于如下形式:

const stores = {
mainStore, userStore, commonStore
};
ReactDOM.render((
<Provider {...stores}>
<App />
</Provider>
), document.getElementById('container'));

2、@inject

inject是一个高阶组件,作用是将组件连接到提供的stores。具体说是与Provider结合使用,从Provider提供给应用的state数据中选取所需数据,以props的形式传递给目标组件。用法为:

inject(stores)(component)
@inject(stores) class Component...

对应上节的例子,App内使用的组件User使用@inject方式为:

import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import {Button} from 'antd';
import './style.css';
@inject( 'userStore')
@observer
export default class User extends Component{
constructor(props){
super(props);
this.state = {};
}
render(){
// 可以以this.props.userStore形式获取store内state
const {user} = this.props.userStore;
// 以.形式使用对象的属性值
return(
<div className='user'>
<div className='user_list'>name:{user.name}</div>
<div className='user_list'>role:{user.name}</div>
<div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
<Button type='primary' onClick={() => this.props.userStore.changeUser()}>Change User</Button>
</div>
);
}
}

3、react-mobx实践示例

我写了一个react-mobx的简单demo,地址为:https://github.com/smk108/mobx_demo ,

demo的结构为:

依据组件的划分使用了3个store,需要说明的是我是以class的形式创建的store,store中export的是store class的instance,例如第一节中userStore的形式:

const userStore = new userStoreClass();
export default userStore;

在React中使用Mobx的方式有很多,Mobx不会强制要求以某种方式使用它,我在demo中使用的方式仅仅是其中一种。事实上,你可以以任何你喜欢并且能生效的方式使用它。在下一篇《Mobx定义数据存储》中会介绍、对比文档推荐使用的数据存储和我的demo中使用的数据结构间的不同。

4、可观察的局部组件状态

Mobx允许在响应式React组件内使用自由地使用状态,意味着我们可以将一些状态像普通React组件一样管理,例如对上面提到的demo中User组件做如下修改:

import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import {Button} from 'antd';
import Timer from '../Timer';
import './style.css'; @inject( 'userStore')
@observer
export default class User extends Component{
constructor(props){
super(props);
this.state = {
userChangeTimes: 0
};
} handleChangeUser(){
this.props.userStore.changeUser();
let {userChangeTimes} = this.state;
userChangeTimes ++ ;
this.setState({userChangeTimes});
} render(){
const {user} = this.props.userStore;
return(
<div className='user'>
<div className='user_list'>name:{user.name}</div>
<div className='user_list'>role:{user.name}</div>
<div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
<div>user change times: {this.state.userChangeTimes}</div>
<Button type='primary' onClick={this.handleChangeUser.bind(this)}>Change User</Button>
<Timer />
</div>
);
}
}

User组件state的userChangeTimes可以使用setState进行修改,一切都和不使用Mobx时一样。但是,推荐响应式组件应该没有或很少有状态,因为在与其他组件共享的对象中封装(视图)状态通常更方便。针对响应式组件需要维护单独状态的情况,Mobx为我们提供了更加方便的一种方式-可观察的局部组件状态。

Mobx允许使用@observable在React组件内引入可观察属性,意味着我们不需要通过React 的冗长和强制性的 setState 机制也可以在组件中拥有功能同样强大的本地状态(local state)。

用法如下(使用demo中Timer组件举例):

import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import {observable, action} from "mobx";
import './style.css'; @inject('commonStore')
@observer
export default class Timer extends Component{
constructor(props){
super(props);
this.state = {};
}
@observable secondsPassed = 0; componentWillMount(){
this.props.commonStore.startTime();
this.timer = setInterval(this.handleChangeSecondsPassed,1000);
} @action.bound handleChangeSecondsPassed(){
this.secondsPassed ++;
} render(){
const {time} = this.props.commonStore;
return(
<div className='time_content'>
<div>{time}</div>
<div>Seconds passed:{this.secondsPassed}</div>
</div>
);
}
}

secondsPassed作为组件内可观察的局部状态,不使用setState也触发UI的响应。

需要注意的是:

可观察局部状态会被render提取调用;
可观察局部状态的修改会触发React的componentWillUpdate和componentDidUpdate生命周期,不会触发其它的生命周期;
如果你需要使用React的其它生命周期方法,请使用基于state的常规React API;
5、生命周期钩子

当使用mobx-react时可以定义一个新的生命周期钩子函数componentWillReact,当组件因为它观察的状态发生改变时,组件会重新渲染,这时componentWillReact会触发,可以帮助追溯渲染并找到导致渲染的动作(action)。

修改demo中User组件举例如下:

import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import {Button} from 'antd';
import Timer from '../Timer';
import './style.css'; @inject( 'userStore')
@observer
export default class User extends Component{
constructor(props){
super(props);
this.state = {
userChangeTimes: 0
};
} handleChangeUser(){
this.props.userStore.changeUser();
let {userChangeTimes} = this.state;
userChangeTimes ++ ;
this.setState({userChangeTimes});
} componentWillReact() {
console.log("I will re-render, since the user has changed!");
} render(){
const {user} = this.props.userStore;
return(
<div className='user'>
<div className='user_list'>name:{user.name}</div>
<div className='user_list'>role:{user.name}</div>
<div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
<div>user change times: {this.state.userChangeTimes}</div>
<Button type='primary' onClick={this.handleChangeUser.bind(this)}>Change User</Button>
<Timer />
</div>
);
}
}

需要注意的是:

componentWillReact 不接收参数;
componentWillReact 初始化渲染前不会触发 (使用 componentWillMount 替代);
componentWillReact 对于 mobx-react@4+, 当接收新的 props 时并在 setState 调用后会触发此钩子;
像User组件内通过setState修改userChangeTimes也会触发此钩子;
6、React优化

本小节介绍几项基本的React优化策略,有些是基于在React中使用Mobx时特有的策略,有些是会用React通用的策略。

使用大量的小组件
@observer 组件会追踪它们使用的所有值,并且当它们中的任何一个改变时重新渲染。 所以你的组件越小,它们需要重新渲染产生的变化则越小;这意味着用户界面的更多部分具备彼此独立渲染的可能性。

在专用组件中渲染列表(避免多个组件受影响,一起重新渲染)
不要使用数组的索引作为 key(虚拟dom)
不用使用数组索引或者任何将来可能会改变的值作为 key

晚一点使用间接引用值
使用 mobx-react 时,推荐尽可能晚的使用间接引用值。 这是因为当使用 observable 间接引用值时 MobX 会自动重新渲染组件。 如果间接引用值发生在组件树的层级越深,那么需要重新渲染的组件就越少。

[Web 前端] mobx教程(三)-在React中使用Mobx的更多相关文章

  1. 推荐20个很有帮助的 Web 前端开发教程

    在平常的搜索中,我碰到过很多有趣的信息,应用程序和文档,我把它们整理在下面这个列表.这是收藏的遇到的有用内容的一个伟大的方式,可以在你需要的时候方便查阅.相信你会在这个列表中发现对你很有用的资料. 您 ...

  2. Web前端入门教程之浏览器兼容问题及解决方法

    JavaScript 被称为JS,是作为浏览器的内置脚本语言,为我们提供操控浏览器的能力,可以让网页呈现出各种特殊效果,为用户提供友好的互动体验.JS是Web前端入门教程中的重点和难点,而浏览器兼容性 ...

  3. 推荐20个很有帮助的web前端开发教程

    1. CSS Vocabulary 一个伟大的指向和点击的小应用程序,让你加快速度掌握 CSS 语法的各个不同部分,学习各个属性的正确的名称. 2. Liquidapsive 一个简单的信息化布局,通 ...

  4. 推荐20个非常有帮助的web前端开发教程

    1. CSS Vocabulary 一个伟大的指向和点击的小应用程序,让你加高速度掌握 CSS 语法的各个不同部分,学习各个属性的正确的名称. 2. Liquidapsive 一个简单的信息化布局.通 ...

  5. web前端该怎么入门?web前端入门教程(非常详细)

    初学编程的小伙伴经常会遇到的问题,1.没资源 2.没人带 3.不知道从何开始 ,小编也是从新手期过来的,所以很能理解萌新的难处,现在整理一些以前自己学习的一些资料送给大家,希望对广大初学小伙伴有帮助! ...

  6. web前端开发教程系列-2 - 前端开发书籍分享(转)

    目录: 前言 一. CSS 二. JavaScript 三. jQuery 四. 后记   前言 前端书籍在每个商城或书架上面都是琳琅满目,很多初学者又不能很好的判断书的质量或层次.因为今天给同学们分 ...

  7. web前端开发教程系列-2 - 前端开发书籍分享

    目录: 前言 一. CSS 二. JavaScript 三. jQuery 四. 后记   前言 前端书籍在每个商城或书架上面都是琳琅满目,很多初学者又不能很好的判断书的质量或层次.因为今天给同学们分 ...

  8. web前端开发教程系列-4 - 前端开发职业规划

    前言 关于我:小天 1). 架构师,项目经理,产品经理 2). 中间件研发 3). VPCC 云计算基础平台管理 4). 智慧旅游 5). 智慧教育 6). 一次失败的创业体验(爱邂逅网) 一. 在开 ...

  9. web前端学习(三)css学习笔记部分(8)-- SVN的介绍和应用、CSS动画效果、CSS3布局属性全接触

    15.SVN的介绍和应用 15.1.SVN的介绍和应用课程概要 将代码进行集中管理,有版本号的进行迭代,方便集体工作的build流程 15.2.SVN的介绍 SVN是Subversion的简称,是一个 ...

随机推荐

  1. 本地化KendoUI

    <!doctype html> <html>     <head>         <title>Kendo UI Web</title> ...

  2. css图片根据div宽高比例自适应

    1.div布局 <div class="card-img-show"> <div class="upload-img-conss" > ...

  3. JSP页面分页显示数据

    效果如上图所示!最多显示10条:完整jsp和后台代码如下: <%@ page contentType="text/html;charset=UTF-8" %> < ...

  4. Python之IO编程——文件读写、StringIO/BytesIO、操作文件和目录、序列化

    IO编程 IO在计算机中指Input/Output,也就是输入和输出.由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘.网络等,就需要IO接口.从 ...

  5. Scala-Unit6-final/type关键字、样例类&样例对象

    一.关键字 1.final关键字 用final修饰的类:不能被继承 用final修饰的方法:不能被重写 注意:(1)在Scala中变量不需要用final修饰,因为val与var已经限制了变量是否可变 ...

  6. 001.Linux开机启动过程

    相关Linux启动过程解析,此作为通用启动参考:

  7. 浅谈云计算SPI(SaaS、PaaS、IaaS)

    The other day, I arrived at the SAP LABS CHINA for interview with my pleasure. That gave me a chance ...

  8. CCF-炉石传说

    #include<cstdio> typedef struct { int attack,health; } cus; cus info[][]; ]; ; void summon(); ...

  9. java之XML

    //转为XML格式 public static String ArrayToXml(Map<String, String> arr) { String xml = "<xm ...

  10. Idea创建一个Springboot单模块项目

    1.打开IDEA,创建新项目,选择Spring Initializr,选择SDK为你的java版本. 2.点击下一步,输入Artifact 3.点击下一步,选择web 4.finish 5.完成后id ...