使用actions
更新狀態(tài)
用法:
-
action
注解(es
規(guī)范中稱之為裝飾器,感覺Mobx
的文檔描述不是很準確忿墅,但是后面翻譯已原文為準) action(fn)
action(name, fn)
所有的應(yīng)用程序都有action
融求。一個action
是用于修改state
的任意一段代碼片段咬像。原則上,action
總是響應(yīng)event
而觸發(fā)生宛。例如:點擊了按鈕县昂、輸入一些內(nèi)容、接收到新的websocket
推送消息等等陷舅。
MobX
要求你聲明動作七芭,盡管makeAutoObservable
可以自動完成很多工作。
Actions
能夠幫助你更好的組織代碼并且提供以下的性能優(yōu)勢:
-
action
在transactions
中執(zhí)行蔑赘。在最外層的action
完成之前,不會更新任何觀察者(比如computed
的值预明,或者使用了observer
的react
組件)缩赛,以確保在動作完成之前,動作的過程中產(chǎn)生的中間值或不完整值對應(yīng)用程序的其余部分不可見撰糠。 - 默認情況下酥馍,不允許在
action
之外的地方更新state
(這一點和之前的版本有區(qū)別,之前的版本需要手動開啟嚴格模式, 具體可參考changelog)阅酪。這有助于清楚的定位觸發(fā)state
更新在代碼庫中的位置旨袒。 -
action
注解應(yīng)該只在修改state
的函數(shù)上使用。那些用來派生出其他的信息函數(shù)(比如 查詢特定的值术辐,過濾等等)不應(yīng)該使用action
注解砚尽,這樣Mobx
能夠跟蹤這些函數(shù)的調(diào)用(其實這里的情況應(yīng)該使用computed
)
makeObserable & makeAutoObservable & action.bound & arrow function
使用action
包裹函數(shù)
為了盡可能利用Mobx
的事務(wù)特性,action
應(yīng)該盡可能的向外傳遞(最開始意圖觸發(fā)修改state
的函數(shù))辉词。如果一個類的方法修改的state
,最好將其標記為action
必孤。最好將事件處理函數(shù)標記為action
,因為它是最外層的事務(wù)瑞躺。
隨后調(diào)用兩個動作的一個未被標記action
的事件處理函數(shù)將會生成兩個事務(wù)敷搪。
為了方便創(chuàng)建基于事件回調(diào)的動作兴想,action
不僅僅是一個注解,也是一個高階函數(shù)赡勘。它可以傳入一個函數(shù)作為參數(shù)來調(diào)用嫂便。在那種場景下,它將返回一個具有同樣簽名的包裝action
闸与。
例如在React
里毙替,一個onClick
處理器可以如下包裝:
const ResetButton = ({ formState }) => (
<button
onClick={action(e => {
formState.resetPendingUploads()
formState.resetValues()
e.stopPropagation()
})}
>
Reset form
</button>
)
為了調(diào)試的目的,我們建議或者命名被包裹的函數(shù),或者傳入一個name
作為action
的第一個參數(shù)几迄。
NOTE: actions
不會被追蹤蔚龙。
actions
另一個特性是他們不會被追蹤。
當從副作用或計算值內(nèi)部調(diào)用action
時(非常罕見S承病)木羹,action
讀取的observable
值不會被計入派生的依賴項。
makeAutoObservable
解孙,extendObservable
和observable
使用一種稱為autoAction
的特殊動作坑填,它將在運行時確定函數(shù)是derivation
還是action
。
action.bound
使用:
-
action.bound (annotation)
action.bound
注解可以用于自動綁定一個方法到正確的實例上(保證this
上下文的引用正確性),以便始終正確的將this
綁定到函數(shù)內(nèi)部弛姜。
TIP
相比action.bound
脐瑰,更喜歡箭頭函數(shù)(推薦使用箭頭函數(shù)的方式來綁定上下文)
如果你想要使用makeAutoObservable
結(jié)合綁定 actions
, 通常使用箭頭函數(shù)會更簡單廷臼。
import { makeAutoObservable } from "mobx"
class Doubler {
value = 0
constructor(value) {
makeAutoObservable(this)
}
increment = () => {
this.value++ 苍在;勞動法非法。
KH
this.value++
}
}
runInAction
用法:
- runInAction(fn)
使用這個輔助方法來創(chuàng)建一個立即執(zhí)行的臨時aciton
,在異步流程中會非常有用荠商。
Asynchronous actions
本質(zhì)上寂恬,在Mobx中異步流程不需要任何特殊的對待,因為所有的reactions
將會自動發(fā)生更新莱没,而不管它們在何時被引起的初肉。而且,由于可觀察對象是可變的饰躲,因此在整個操作過程中保持對它們的引用通常是安全的牙咏。然后,在一個異步流程中嘹裂,更新observable
的每一步都應(yīng)該被標記為action
妄壶。可以通過利用上述API以多種方式實現(xiàn)這一點焦蘑,如下所示盯拱。
例如,當處理promises
的時候,更新state
的程序應(yīng)該被action
包裹(還記的action可以作為一個函數(shù)直接調(diào)用嗎狡逢?)或者應(yīng)該是一個action
,如下所示:
Wrap handlers in action
Promise resolution handlers are handled in-line, but run after the original action finished, so they need to be wrapped by action:
import { action, makeAutoObservable } from "mobx"
class Store {
githubProjects = []
state = "pending" // "pending", "done" or "error"
constructor() {
makeAutoObservable(this)
}
fetchProjects() {
this.githubProjects = []
this.state = "pending"
fetchGithubProjectsSomehow().then(
action("fetchSuccess", projects => {
const filteredProjects = somePreprocessing(projects)
this.githubProjects = filteredProjects
this.state = "done"
}),
action("fetchError", error => {
this.state = "error"
})
)
}
}
使用 flow替代 of async / await {??}
用法:
- flow (注解)
- flow(function* (args) { }
flow 是async / await的可選替代方案宁舰,它使使用MobX操作更加容易。generator function
功能作為其唯一輸入奢浑。在生成器內(nèi)部蛮艰,您可以通過yield somePromse
實現(xiàn)同步鏈寫法(使用yield somePromise
替換wait somePromise
)。然后雀彼,flow
將確保生成器在產(chǎn)生的承諾解決時繼續(xù)運行或拋出壤蚜。
所有,flow
是async / await
的替代方案徊哑,這種方式不需要進一步使用action
包裹袜刷。可以按照如下步驟應(yīng)用:
- 使用
flow
包裹你的異步函數(shù) - 使用
function *
替換async
- 使用
yield
替換await
代碼如下:
import { flow, makeAutoObservable, flowResult } from "mobx"
class Store {
githubProjects = []
state = "pending"
constructor() {
makeAutoObservable(this, {
fetchProjects: flow
})
}
// Note the star, this a generator function!
*fetchProjects() {
this.githubProjects = []
this.state = "pending"
try {
// Yield instead of await.
const projects = yield fetchGithubProjectsSomehow()
const filteredProjects = somePreprocessing(projects)
this.state = "done"
this.githubProjects = filteredProjects
} catch (error) {
this.state = "error"
}
}
}
const store = new Store()
const projects = await flowResult(store.fetchProjects())
需要注意的是莺丑,上面例子中的flowResult
函數(shù)只有當使用typescript
的時候才需要著蟹。
因為使用flow
裝飾一個方法,它將會使用一個promise
包裹返回的generator
,但是typescript
并不知道這種轉(zhuǎn)換梢莽,所以flowResult
將確保typescript
知道這種類型改變萧豆。
makeAutoObservable
將會自動推動generator
函數(shù)為flow
。
NOTE 也可以在對象的屬性上使用flow
類似于action
, flow
也可以也可以直接包裹一個函數(shù)使用昏名,上面的例子也可以是使用如下代碼書寫涮雷。
import { flow } from "mobx"
class Store {
githubProjects = []
state = "pending"
fetchProjects = flow(function* (this: Store) {
this.githubProjects = []
this.state = "pending"
try {
// yield instead of await.
const projects = yield fetchGithubProjectsSomehow()
const filteredProjects = somePreprocessing(projects)
this.state = "done"
this.githubProjects = filteredProjects
} catch (error) {
this.state = "error"
}
})
}
const store = new Store()
const projects = await store.fetchProjects()
這樣寫法的好處是,如果你使用typescript
轻局,不再需要flowResult
洪鸭,缺點是傳入this
,確保上下文引用正確仑扑。
補充:你可以選擇使用bind
來綁定你的上下文卿嘲。
import { flow } from "mobx"
class Store {
githubProjects = []
state = "pending"
fetchProjects = flow(function* () {
this.githubProjects = []
this.state = "pending"
try {
// yield instead of await.
const projects = yield fetchGithubProjectsSomehow()
const filteredProjects = somePreprocessing(projects)
this.state = "done"
this.githubProjects = filteredProjects
} catch (error) {
this.state = "error"
}
})
}.bind(this); // here we use bind.
const store = new Store()
const projects = await store.fetchProjects()
Cancelling flows{??}
flow
的另外一個好處是可以取消。flow
的返回值是一個promise
,這個返回值是通過generator
函數(shù)的返回值夫壁。這個返回的promise
有個額外的cancel()
方法,可以用來打斷正在執(zhí)行的generator
并且取消它沃疮。try / finally
的代碼將任然會被執(zhí)行盒让。
Disabling mandatory actions {??}
默認,Mobx6以及之后的版本將會要求你使用actions
來改變state
司蔬。但是你也可以使用Mobx 配置邑茄,禁用這一行為。查看enforceActions
章節(jié)俊啼。比如,這在單元測試配置中非常有用,單元測試中這些警告沒有什么價值同木。