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:

  1. import {observable, computed, action} from 'mobx';
  2. class userStoreClass {
  3. @observable user = {
  4. name: 'admin',
  5. role: '管理员'
  6. };
  7. count = 0;
  8. @computed get userName(){
  9. return this.user.name;
  10. }
  11. @action changeUser(){
  12. if(this.count % 2 === 1){
  13. this.user = {
  14. name: 'admin',
  15. role: '管理员'
  16. };
  17. }else{
  18. this.user.name = 'guest';
  19. this.user.role = '访客';
  20. this.user.isGuest = 'true';
  21. }
  22. this.count ++;
  23. }
  24. }
  25. const userStore = new userStoreClass();
  26. export default userStore;

使用Provider传递store的方式为:

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

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

  1. const stores = {
  2. mainStore, userStore, commonStore
  3. };
  4. ReactDOM.render((
  5. <Provider {...stores}>
  6. <App />
  7. </Provider>
  8. ), document.getElementById('container'));

2、@inject

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

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

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

  1. import React, {Component} from 'react';
  2. import {inject, observer} from 'mobx-react';
  3. import {Button} from 'antd';
  4. import './style.css';
  5. @inject( 'userStore')
  6. @observer
  7. export default class User extends Component{
  8. constructor(props){
  9. super(props);
  10. this.state = {};
  11. }
  12. render(){
  13. // 可以以this.props.userStore形式获取store内state
  14. const {user} = this.props.userStore;
  15. // 以.形式使用对象的属性值
  16. return(
  17. <div className='user'>
  18. <div className='user_list'>name:{user.name}</div>
  19. <div className='user_list'>role:{user.name}</div>
  20. <div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
  21. <Button type='primary' onClick={() => this.props.userStore.changeUser()}>Change User</Button>
  22. </div>
  23. );
  24. }
  25. }

3、react-mobx实践示例

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

demo的结构为:

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

  1. const userStore = new userStoreClass();
  2. export default userStore;

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

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

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

  1. import React, {Component} from 'react';
  2. import {inject, observer} from 'mobx-react';
  3. import {Button} from 'antd';
  4. import Timer from '../Timer';
  5. import './style.css';
  6.  
  7. @inject( 'userStore')
  8. @observer
  9. export default class User extends Component{
  10. constructor(props){
  11. super(props);
  12. this.state = {
  13. userChangeTimes: 0
  14. };
  15. }
  16.  
  17. handleChangeUser(){
  18. this.props.userStore.changeUser();
  19. let {userChangeTimes} = this.state;
  20. userChangeTimes ++ ;
  21. this.setState({userChangeTimes});
  22. }
  23.  
  24. render(){
  25. const {user} = this.props.userStore;
  26. return(
  27. <div className='user'>
  28. <div className='user_list'>name:{user.name}</div>
  29. <div className='user_list'>role:{user.name}</div>
  30. <div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
  31. <div>user change times: {this.state.userChangeTimes}</div>
  32. <Button type='primary' onClick={this.handleChangeUser.bind(this)}>Change User</Button>
  33. <Timer />
  34. </div>
  35. );
  36. }
  37. }

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

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

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

  1. import React, {Component} from 'react';
  2. import {inject, observer} from 'mobx-react';
  3. import {observable, action} from "mobx";
  4. import './style.css';
  5.  
  6. @inject('commonStore')
  7. @observer
  8. export default class Timer extends Component{
  9. constructor(props){
  10. super(props);
  11. this.state = {};
  12. }
  13. @observable secondsPassed = 0;
  14.  
  15. componentWillMount(){
  16. this.props.commonStore.startTime();
  17. this.timer = setInterval(this.handleChangeSecondsPassed,1000);
  18. }
  19.  
  20. @action.bound handleChangeSecondsPassed(){
  21. this.secondsPassed ++;
  22. }
  23.  
  24. render(){
  25. const {time} = this.props.commonStore;
  26. return(
  27. <div className='time_content'>
  28. <div>{time}</div>
  29. <div>Seconds passed:{this.secondsPassed}</div>
  30. </div>
  31. );
  32. }
  33. }

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

需要注意的是:

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

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

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

  1. import React, {Component} from 'react';
  2. import {inject, observer} from 'mobx-react';
  3. import {Button} from 'antd';
  4. import Timer from '../Timer';
  5. import './style.css';
  6.  
  7. @inject( 'userStore')
  8. @observer
  9. export default class User extends Component{
  10. constructor(props){
  11. super(props);
  12. this.state = {
  13. userChangeTimes: 0
  14. };
  15. }
  16.  
  17. handleChangeUser(){
  18. this.props.userStore.changeUser();
  19. let {userChangeTimes} = this.state;
  20. userChangeTimes ++ ;
  21. this.setState({userChangeTimes});
  22. }
  23.  
  24. componentWillReact() {
  25. console.log("I will re-render, since the user has changed!");
  26. }
  27.  
  28. render(){
  29. const {user} = this.props.userStore;
  30. return(
  31. <div className='user'>
  32. <div className='user_list'>name:{user.name}</div>
  33. <div className='user_list'>role:{user.name}</div>
  34. <div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
  35. <div>user change times: {this.state.userChangeTimes}</div>
  36. <Button type='primary' onClick={this.handleChangeUser.bind(this)}>Change User</Button>
  37. <Timer />
  38. </div>
  39. );
  40. }
  41. }

需要注意的是:

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. jenkins(4): jenkins 插件

    1.  jenkins插件下载镜像加速 jenkins插件清华大学镜像地址 https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-ce ...

  2. jquery|js|jq常用正则

    var mobReg=/^1[34578]\d{9}$/; //手机号 if (!mobReg.test(mob)) { mui.alert("请填写正确手机号!"," ...

  3. BZOJ3262/洛谷P3810 陌上花开 分治 三维偏序 树状数组

    原文链接http://www.cnblogs.com/zhouzhendong/p/8672131.html 题目传送门 - BZOJ3262 题目传送门 - 洛谷P3810 题意 有$n$个元素,第 ...

  4. Date、Calendar、DateFormat类

    Date类与Calendar类之间的转换 package date; import java.util.Calendar; import java.util.Date; public class Da ...

  5. 1402 后缀数组 (hash+二分)

    描述 后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围.在本题中,我们希望使用快排.Hash与二分实现一个简单的 O(n log^2⁡n ) 的后缀数组 ...

  6. POJ 1988 Cube Stacking 【带权并查集】

    <题目链接> 题目大意: 有几个stack,初始里面有一个cube.支持两种操作: 1.move x y: 将x所在的stack移动到y所在stack的顶部. 2.count x:数在x所 ...

  7. java设计模式之-观察者模式(发布-订阅模式)

    1.观察者模式定义  观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象. 这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己. 2.观察者模式结构 ...

  8. DRF的视图

    DRF的视图 APIView 我们django中写CBV的时候继承的是View,rest_framework继承的是APIView,那么他们两个有什么不同呢~~~ urlpatterns = [    ...

  9. JS获取IOS版本号

    var str= navigator.userAgent.toLowerCase(); var ver=str.match(/cpu iphone os (.*?) like mac os/); if ...

  10. ubuntu16 64 搭建lnmp环境

    //安全设置linux(ubuntu16 64) 安全设置1.修改ssh端口 vi /etc/ssh/sshd_config 如果用户想让22和60000端口同时开放,只需在/etc/ssh/sshd ...