[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 會自動重新渲染組件。 如果間接引用值發生在組件樹的層級越深,那么需要重新渲染的組件就越少。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM