當一個 component 需要存取修改一份資料 / 狀態的時候,通常我們會把這份資料 / 狀態做成 state,讓該 component 自己維護資料的內容。
然而常常會出現多個 component 需要存取修改同一份資料 / 狀態的狀況。在這種情形下應該怎麼處理呢?是否要複製一份相同的資料作為其他 component 的 state 呢?
答案是:State 應該要保持一份就好,另外,此 state 應要被提升到最靠近這些 component 的共同祖先 component 上。
在解釋為什麼要這樣做之前,先讓我們了解一下 React 對資料處理理念。
React 十分重視對於資料與狀態的處理方式。
現代的網頁中,為了講求更好的使用者體驗,並滿足大型網站的開發需求,資料與狀態處理變的日益困難。而一個好的資料處理方式可以大大降低維護專案的難度。
為了達到易維護資料與狀態的目的,React 提倡以下兩個資料與狀態的處理理念:
以下將逐一介紹。
第一個理念是 Single source of truth,React 主張一份資料只應該維護一份。
理想上,一份資料 Single source of truth 的範圍應該橫跨整個專案。
也就是說,不論對於下層、同層、上層 component 都不應該重複擁有同一份資料。
另外,能被計算出來的資料不應該另外存成另一份資料,只應保留最原始的那份即可。
舉例來說,如果今天有兩份清單,一份是原始清單,一份是使用者搜尋後過濾出來的清單,則應該只保留原始清單就好。因為只要有原始清單,我們就隨時隨地可以計算出任何使用者想要過濾的內容,因此,同時存下搜尋過濾出來的清單就較沒有必要。
Single source of truth 的優點會有幾個:
由於只要維護一份資料,因此更新資料時會變的較輕鬆
避免資料不一致的問題
當同樣資料的份數變多時,很容易發生的問題就是其中幾份資料漏改或錯改,導致資料不一致的問題。
由於只有一份資料,因此取用資料時不會有不知道應該取用哪份資料的問題
承接上一點,當有多份同樣卻不統一的資料時,就很難知道哪份資料才是可信任的。常見的狀況是每個地方都取用不同份的資料,使整個專案中的資料混亂不堪。
Single source of truth 也有缺點,例如可能會導致效能較差,因為使用資料時常常都需要被重新計算,不能暫存起來。但整體來說,single source of truth 絕對是讓資料易於維護的關鍵點之一。
第二個理念是 Top-down data flow,React 主張一份資料只應該由上而下傳遞,且一份資料只應在一個地方修改。
也就是說,一份資料被創建出來後就只能往下層 component 傳遞,無法往上層 component 傳遞。整個資料傳遞呈現由上至下的流向,所以稱做 top-down flow。
如果今天下層 component 想要更改資料,則需往上層 component 透過 event 發送 "更改請求" 。然而最後決定是否修改資料的還是持有資料的 component。
Top-down flow 會有以下維護上的好處:
因為資料流必定是由上至下的,因此只要往上找就可找到資料源頭
資料必須在創建的地方修改,如此可以大大減少追查資料更新位置的成本
如果一份資料可以任何 component 中修改,則資料更新錯誤時找到實際更新資料的位置會變的十分困難,因為可能性太多,每個地方都需要確認是否有改動資料,造成維護上極大的困難。
在 React 中,top-down data flow 的實踐方式就是 state 與 prop 以及 event。
State(資料)會由一個 component 創建出來,並可能作為下層 component 的 prop 傳入。另一方面,接收 prop 的 component 只能取用資料而不能修改。如果下層的 component 有任何修改 state 的需求,則需透過 event 向上傳遞請求。創建 state 的 component 收到 event 後就可以依照 event 的內容選擇如何更新 state。整個 top-down data flow 就這樣被實踐出來。
雖然缺點是樣板(Boilerplate)程式碼會比較多,但可以有效減少 bug 並使追查資料流向變得很簡單。
總結 React 處理資料的理念,single source of truth 是限制資料的存在份數,而 top-down flow 是限制資料的流向與改動資料的地方。限制了過多資料處理方式的可能性後,將大大降低維護的難易度。
回到開頭所問的,當多個 component 需要修改同一個資料(state)時,應該怎麼處理呢?回顧一下剛剛介紹的內容,答案應該呼之欲出了。
資料應該要同時符合兩個概念:
也就是說:
所以結論就是:
應該把共有資料提升到有使用到此資料的 component 的共有祖先 state 中,以達到 Single Souce Of Truth 的概念。
接著,如果下層 component 需要的話,就使用 prop 的方式將 state 資料傳遞下去。
如果下層 component 需要通知上層 component 改動,就透過 event 發送通知。
如此就可以同時達到 Top-down data flow 了。
而這就是 提升 State 的概念以及為何這麼做的原因。
從上面的介紹可以知道,只要有多個 component 共用 state 的狀況,就應該把此 state 提升到 component 的共用祖先上。
然而有時候這種功能也可能藉由 Context 或是 Redux 等管理工具達成。
因為提升 state 技巧結合了 top down data flow 與 single source of truth 的概念,因此提升 state 技巧擁有兩項理念的所有優點,包括:
同時,提升 state 也會包含兩項理念的缺點:
每次資料都必須重新計算,在某些情況下可能效能較差(Single source of truth)
Boilerplate 程式較多,傳遞太多層時會造成維護不易(Top-down data flow)
這個缺點可以用 Context 來解決。Context 將在往後章節提及。
在這個章節中,我們介紹了提升 state 的概念以及為什麼這麼做的原因。
需要提升 state 是因為 React 有兩個處理資料的理念:
透過實踐這兩個理念可以大大提高程式的可維護性,而提升 state 就是這兩個概念的實踐方式。
下個章節中,我們將使用範例實際示範如何提升 state。