copy from : https://blog.csdn.net/smk108/article/details/85053903css
Mobx提供了一個mobx-react包幫助開發者方便地在React中使用Mobx,mobx-react中有observer、Provider、inject幾個經常使用的api。在《mobx系列(二)-mobx主要概念》中咱們已經介紹過observer,本文介紹下inject、Provider,以及Mobx如何與React結合使用。react
一、Providergit
Provider是一個React組件,使用React的上下文(context)機制,能夠用來向下傳遞stores,即把state傳遞給其子組件。github
例如,有以下形式的一個store:api
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,可使用相似於以下形式:antd
const stores = {
mainStore, userStore, commonStore
};
ReactDOM.render((
<Provider {...stores}>
<App />
</Provider>
), document.getElementById('container'));
二、@inject數據結構
inject是一個高階組件,做用是將組件鏈接到提供的stores。具體說是與Provider結合使用,從Provider提供給應用的state數據中選取所需數據,以props的形式傳遞給目標組件。用法爲:dom
inject(stores)(component)
@inject(stores) class Component...
對應上節的例子,App內使用的組件User使用@inject方式爲:ide
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> ); } }
三、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中使用的數據結構間的不一樣。
四、可觀察的局部組件狀態
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;
五、生命週期鉤子
當使用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也會觸發此鉤子;
六、React優化
本小節介紹幾項基本的React優化策略,有些是基於在React中使用Mobx時特有的策略,有些是會用React通用的策略。
使用大量的小組件
@observer 組件會追蹤它們使用的全部值,而且當它們中的任何一個改變時從新渲染。 因此你的組件越小,它們須要從新渲染產生的變化則越小;這意味着用戶界面的更多部分具有彼此獨立渲染的可能性。
在專用組件中渲染列表(避免多個組件受影響,一塊兒從新渲染)
不要使用數組的索引做爲 key(虛擬dom)
不用使用數組索引或者任何未來可能會改變的值做爲 key
晚一點使用間接引用值使用 mobx-react 時,推薦儘量晚的使用間接引用值。 這是由於當使用 observable 間接引用值時 MobX 會自動從新渲染組件。 若是間接引用值發生在組件樹的層級越深,那麼須要從新渲染的組件就越少。