1、老的用法
mobx是一個使用十分普遍的狀態(tài)管理工具,在實際的開發(fā)過程中我們常常搭配react進行使用。在一些比較大的項目中,部分變量需要反復(fù)向下層組件進行傳遞,如果使用傳統(tǒng)的組件props進行實現(xiàn),層層包裹,未免過于繁瑣,react官方針對這種場景,推出了context來進行上下文跨組件傳遞。而mobx也立足于react的context實現(xiàn)了inject語法,通過簡潔的api,可以在任何想要使用全局狀態(tài)的地方注入變量。為了方便進行全局的狀態(tài)管理,往往設(shè)定一個全局的store,其中定義一些全局都會使用到的變量,進行狀態(tài)控制,比較典型的例子如下:
import React from 'react';
import { inject, observer } from 'mobx-react';
import comA from 'comA ';
import comB from 'comB';
@inject('globalStore')
@observer
class Test extends React.Component{
render() {
const PanelContent = {
'comA': comA,
'comB': comB
}
const ShowContent = PanelContent[this.props.globalStore.funcType];
return (
<React.Fragment>
<ShowContent />
</React.Fragment>
)
}
}
export default Test ;
這里使用decorator裝飾器語法,通過字符串的方式注入全局store,組件的this上將會添加屬性globalStore,通過store中的funcType的變量來控制顯示的組件,該變量變化時,渲染的組件也會根據(jù)條件變化(通過observer裝飾器實現(xiàn))。其外圍容器的寫法通常如下:
import React from 'react';
import { Provider, observer } from 'mobx-react';
import Test from 'Text';
import globalStorefrom './globalStore';
class parentCom extends React.Component {
const Store = new globalStore({ funcType: 'comA'});
return (
<Provider globalStore={Store}>
<Test />
</Provider>
)
};
export default parentCom;
其父組件通過mobx-react的provider包裹器來將全局的store作為參數(shù)傳入。哪怕嵌套多層,子組件也可直接通過添加inject裝飾器來使用全局store中的變量,就如Test。
2、hooks中的inject
react在最新的16.8中啟用了hooks語法,力推函數(shù)式組件,盡管官方表示class式的組件在后續(xù)版本中并不會廢棄,但是hooks是未來前端框架中組件的發(fā)展方向(最新的Vue也借鑒了react Hook的很多思路),我們需要大膽嘗試新鮮事物。
到mobx官網(wǎng)上發(fā)現(xiàn),幾乎所有的例子都是基于class組件來寫的,并沒有發(fā)現(xiàn)跟react hook搭配使用的內(nèi)容。。。最后在一個不起眼處,找到了一個鏈接,指向mobx-react的遷移文檔。官方操作如下:
import { MobXProviderContext } from 'mobx-react'
function useStores() {
return React.useContext(MobXProviderContext)
}
自己定義一個react hook,讓后就可以在我們自己的組件中使用了:
function useUserData() {
const { user, order } = useStores()
return {
username: user.name,
orderId: order.id,
}
}
const UserOrderInfo = observer(() => {
// Do not destructure data!
const data = useUserData()
return (
<div>
{data.username} has order {data.orderId}
</div>
)
})
從官方例子中,我們可以發(fā)現(xiàn)可以棄用inject語法糖,直接通過自定義的useStores,我們就可以實現(xiàn)獲取外層provider的變量并且使用,注意此處不能使用解構(gòu)賦值,否則的話會導(dǎo)致無法實現(xiàn)變量的觀測(即變量改變,頁面顯示沒有同步),如果要實現(xiàn)觀測:
// use mobx-react@6.1.2 or `mobx-react-lite`
import { useObserver } from 'mobx-react'
function useUserData() {
const { user, order } = useStores()
return useObserver(() => ({
username: user.name,
orderId: order.id,
}))
}
const UserOrderInfo = () => {
// this works now just fine
const { username, orderId } = useUserData()
return (
<div>
{username} has order {orderId}
</div>
)
}
如果你還是想要自己手動實現(xiàn)inject方法,那么官方還給了一個簡單的inject組件實現(xiàn):
import { MobXProviderContext } from 'mobx-react'
function inject(selector, baseComponent) {
const component = ownProps => {
const store = React.useContext(MobXProviderContext)
return useObserver(() => baseComponent(selector({ store, ownProps })))
}
component.displayName = baseComponent.name
return component
}
回到我們自己的組件,如果第一部分中的組件,要通過函數(shù)式組件的方式,使用provider提供的全局store要怎么辦呢?
import React from 'react';
import { observer } from 'mobx-react';
import comA from 'comA ';
import comB from 'comB';
import { useStores } from '@utils/index';
function useStores(name) {
return React.useContext(MobXProviderContext)[name];
}
const Test = () => {
const store = useStores('flagStore'); // 手動傳入字符串,選擇要使用的內(nèi)容
const PanelContent = {
'comA': comA,
'comB': comB
}
const ShowContent = PanelContent[store.funcType];
return (
<React.Fragment>
<ShowContent />
</React.Fragment>
)
}
export default observer(Test );
官方例子中的useStores會返回所有在context中的內(nèi)容,也就是所有上級provider中傳遞的內(nèi)容,此處我們通過在自己的實現(xiàn)中傳入一個字符串來控制選取我們需要的內(nèi)容。
如有任何疑問,歡迎留言交流~
————————————————————————————————————
參考文獻:
mobx-react遷移官方文檔:
https://mobx-react.js.org/recipes-migration