【React】798- 使用 React&Mobx 的幾個最佳實踐

Mobx 是我非常喜歡的 React 狀態(tài)管理庫,它非常靈活,同時它的靈活也會給開發(fā)帶來非常多的問題,因此我們在開發(fā)的時候也要遵循一些寫法上的最佳實踐,使我們的程序達到最好的效果。
在 store 中維護業(yè)務(wù)邏輯
盡量不要把業(yè)務(wù)邏輯寫在?React Component?里面。當你把業(yè)務(wù)邏輯寫在組件里面的時候,很難及時定位錯誤的,因為業(yè)務(wù)邏輯分散在各種不同的組件里面,讓你很難來通過行為來定義到底是哪些代碼涉及的這個錯誤,不同組件復(fù)用這些邏輯也很困難。
最好在?stores?中把業(yè)務(wù)邏輯編寫成方法,并在你的?Component?中調(diào)用這些方法。
只允許在 store 中修改屬性
盡量不要在一個?Component?里直接修改一個?store?的屬性。只有?store?本身可以修改他自己的屬性。
當你要改變屬性的時候,請調(diào)用相應(yīng)的?store?方法。不然的話你的屬性修改會散落在各處不受控制,這是很難調(diào)試的。
class?Store?{
??@observable?text;
??
[email protected]
??handleSearch?=?value?=>?{
????this.text?=?value
??}
}
const?store?=?new?Store();
@observer
class?Home?extends?Component?{
??
??handleChanged?=?(event)?=>?{
????store.handleSearch(event.target.value);
??}
??
??render()?{
????return?(
??????<input
????????value={store.searchText}
????????onChange={this.handleChanged}
??????/>
????);
??}
}
所有屬性更改都用 action
使用?action?后,可以清楚的看出哪些代碼可以更改可觀察的變量,并且方便調(diào)試工具給出更多的信息
使用?transaction?可以將多個應(yīng)用狀態(tài)(Observable)的更新視為一次操作,并只觸發(fā)一次監(jiān)聽者(Reactions)的動作(UI更新、網(wǎng)絡(luò)請求等),避免多次重復(fù)渲染。action中封裝了transaction,多次改變@observable變量時,只會重新渲染一次,提高了性能。
class?Store?{
??@observable?name;
??@observable?age;
??@action
??change(name,age){
????this.name?=?name;
????this.age?=?age;
??}
}
從 store 中分離出 API 請求
不要在你的?store?里調(diào)用?API?接口,這會讓它們很難測試,也讓代碼變的更復(fù)雜和耦合。額外建一個類,把?API?接口調(diào)用放進去,并在?store?的構(gòu)造函數(shù)里實例化他們來使用。當你編寫測試代碼時,你可以很容易地模擬這些?api?并把你的模擬?api?實例傳給每一個?store。
class?UserService?{
??fetchUser?=?()?=>?axios.get('/api/user')
}
class?Store?{
??@observable?todos?=?[];
??constructor(userService)?{
????this.userService?=?userService;
??}
??fetchUser?=?async?()?=>?{
????const?todos?=?await?this.userService.fetchUser();
????runInAction(()?=>?{
??????this.todos?=?todos;
????});
??}
}
const?userService?=?new?UserService();
const?store?=?new?Store(userService);
對每一個 component 都聲明 @observer
@observer?可以用來將?React?組件轉(zhuǎn)變成響應(yīng)式組件。它用?mobx.autorun?包裝了組件的?render?函數(shù)以確保任何組件渲染中使用的數(shù)據(jù)變化時都可以強制刷新組件。
給每一個?component?都標注?@observer?,這可以使得他們可以隨著?store prop?的改變而更新。如果子組件沒有標注?@observer?的話,就會導(dǎo)致其父?component?(有?@observer?)刷新。因此我們要盡可能的讓每個子?component?都標注?@observer?,這可以減少不必要的重新渲染。
不要緩存 observables 屬性
Observer?組件只會追蹤在?render?方法中存取的數(shù)據(jù)。如果你從?observable?屬性中提取數(shù)據(jù)并將其緩存在組件里,這樣的數(shù)據(jù)是不會被追蹤的:
class?Store?{
??@observable?name;
??@observable?age;
}
class?Home?extends?React.Component?{
??componentWillMount()?{
????//?錯誤的,info?的更新不會被追蹤
????this.info?=?store.name?+?store.age
??}
??render()?{
????return?<div>{this.info}div>
??}
}
使用 @computed
比如剛剛的例子,使用?@computed?屬性來處理一些涉及多個屬性的邏輯。使用?@computed?可以減少這樣的判斷類業(yè)務(wù)邏輯在組件里面出現(xiàn)的頻率。
class?Store?{
??@observable?name;
??@observable?age;
??@computed?info?=?()?=>?{
????return?this.name?+?this.age;
??}
}
class?Home?extends?React.Component?{
??render()?{
????return?<div>{store.info}div>
??}
}
多編寫受控組件
多編寫可控組件,這樣會大大降低你的測試復(fù)雜度,也讓你的組件易于管理。
當需要追蹤對象屬性時、使用 map
MobX?可以做許多事,但是它無法將原始類型值轉(zhuǎn)變成?observable?(盡管可以用對象來包裝它們)。所以說值不是?observable,而對象的屬性才是。這意味著?@observer?實際上是對間接引用值作出反應(yīng)。所以如果像下面這樣初始化的話,Timer?組件是不會作出任何反應(yīng)的:
ReactDom.render(<Timer?timerData={timerData.secondsPassed}?/>,?document.body)
在這行代碼中,只是?secondsPassed?的當前值傳遞給了?Timer,這個值是不可變值 (JS中的所有原始類型值都是不可變的)。這個值永遠都不會改變,所以?Timer?也永遠不會更新。?secondsPassed?屬性將來會改變,所以我們需要在組件內(nèi)訪問它。或者換句話說: 永遠只傳遞擁有?observable?屬性的對象。
如果你想追蹤對象中每個屬性的變更,可以使用?map:
observable.map(values?)?創(chuàng)建一個動態(tài)鍵的?observable?映射。如果你不但想對一個特定項的更改做出反應(yīng),而且對添加或刪除該項也做出反應(yīng)的話,那么?observable?映射會非常有用。
class?Store?{
[email protected]({secondsPassed:0});
[email protected]
??change(value){
????this.timerData.set('secondsPassed',value);
??}
}
class?Home?extends?React.Component?{
??render()?{
????return?<div>{store.timerData.get('secondsPassed')}div>
??}
}
回復(fù)“加群”與大佬們一起交流學習~
點擊“閱讀原文”查看 80+ 篇原創(chuàng)文章
