React Cascading Effects
在大型專案中,總是無法避免透過 useEffect 可能要去更新某些 State(例如使用者做了一些事,它需要去 trigger API 或是多個頁面的 state 觸發),不過這會讓 State 更新的 flow 變得模糊不清楚,最好的方式仍然是透我 Event 去驅動 State 的更新。
但當無法避免的時候,應盡可能的避免使用多個 useEffect 造成的連環 State 更新。
useEffect(() => {
setXxaState("...")
}, [someState])
useEffect(() => {
setXxbState("...")
}, [xxA])
useEffect(() => {
setXxcState("...")
}, [xxb])
useEffect(() => {
setXxxState("...")
}, [xxa])這會造成 state 的更新難以追蹤並且非常難找到錯誤。
解決方式是可以透過 useReducer 去定義各個 Event 和設定 State 的狀態(status),並將它寫入在同個 useEffect 中。
const stateReducer = (state, action) => {
switch (action.type) {
case "xxx":
return {
...state,
xxx: {}
}
case "ccc":
return {
...state,
xxx: {},
cc: 0
}
default:
break;
}
}
useEffect(() => {
if (state.status === "updateXxx") {
callApi().then(() => {
dispatch("xxx", {status: "ccc"})
})
} else if (state.status = "ccc") {
callApi2().then(() => {
dispatch("xxx", {status: "ooo"})
})
} // ....
}, [state])
這樣 State 的 Flow 會更清楚,也較好進行除錯。
// before ❌
useEffect(() => {
// ...
}, [deps1])
useEffect(() => {
// ...
}, [deps2])
useEffect(() => {
// ...
}, [deps3])
useEffect(() => {
// ...
}, [deps4])
// after ✅
useEffect(() => {
if (state.status === '...') {
} else if (state.status === '...') {
} else if (state.status === '...') {
}
},[state])