• [Web 前端] mobx教程(三)-在React中使用Mobx


    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 会自动重新渲染组件。 如果间接引用值发生在组件树的层级越深,那么需要重新渲染的组件就越少。

  • 相关阅读:
    leetcode-788-Rotated Digits(使用vector替代if else的逐个判断)
    leetcode-771-Jewels and Stones(建立哈希表,降低时间复杂度)
    leetcode-766-Toeplitz Matrix(每一条对角线元素的比较)
    leetcode-762-Prime Number of Set Bits in Binary Representation
    leetcode-747-Largest Number At Least Twice of Others(求vector的最大值和次大值)
    Flink中逻辑计划和物理计划的概念划分和对应关系
    Java线程池中线程的生命周期
    在ServletFilter层返回多字段提示给前端
    JavaBIO利用装饰器模式来组织和扩展接口
    SparkShuffle机制
  • 原文地址:https://www.cnblogs.com/0616--ataozhijia/p/10192700.html
Copyright © 2020-2023  润新知