iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0

在昨天的篇章,介紹了詳細的 state 變化流程。今天則是要來介紹在使用 state 的時候,我們該如何選擇他的架構該長什麼樣子。昨天已經有先提到一些了,今天會再做更詳細的介紹。
今天的文章參考官方文件的:

設計好 state 架構的主要方法

要設計好一個 React 應用程式,好的 state 架構十分重要。好的架構可以幫助減少非預期錯誤,更好的可讀性,讓開發者寫得更順暢。官方文件提供幾個方法可以參考:

  1. 把相關的 state 分群
  2. 避免矛盾的 state
  3. 避免多餘的 state
  4. 避免重複的 state
  5. 避免複雜的巢狀 state

今天就會根據這五項進行詳細的介紹。

把相關的 state 分群

有時候剛開始設計 state 架構的時候,我們可能會不確定會使用到多少個 state。文章就用座標當例子:

const [x, setX] = useState(0);
const [y, setY] = useState(0);

或是

const [position, setPosition] = useState({ x: 0, y: 0 });

這兩個方法都可以使用,但如果我們很常同時改 xy 的話,就變成需要總是寫上 setXsetY,有時候就會不小心漏掉一個導致程式錯誤。所以當我們在開發的時候,發現有哪些 state 是經常需要一起更新的,就可以把它們組合在一起變成一個 state 來利用。

在把 state 組合起來的時候,要特別小心我們在更新的時候不能一次只改裡面其中一個項目:

// 錯誤!會把 Position 改成只剩 x
setPosition({ x: 100 })
// 正確!沒動到的 y 會被保留
setPosition({ ...position, x: 100 })

避免矛盾的 state

這在昨天的文章有提到,在設計 state 的時候,可能會遇到像是 isTypingisSubmitting 同時是 true 的情況發生,但我們的 UI 不應該要有這種情況。這種很常在使用很多 Boolean 的 state 的時候產生,因為兩種 boolean 就要考量四種不同可能。這時候我們可以使用一個 status state 去管理狀態,就不會造成混淆。

避免多餘的 state

當我們可以用其他 state 去推算出想要的資料,就不需要特別再使用 state 去管理。像是文章中的名字當例子,我們有 firstNamelastName 並且想顯示組合在一起的 fullName。由於 fullName 是由 firstNamelastName 組成,就不用特別再用 state 去存,然後在修改的時候去 setFullName,直接寫成 const fullName = firstName + ' ' + lastName 就好。

這也常用在一些公式計算,像是我們知道打擊率是 安打 / 打數,那我們要顯示打擊率就不用特別再多寫個 state 去管理他。

避免重複的 state

有時候我們會需要透過 state 去更改當下選取的資料一個陣列裡的資料,第一個想到的方法就是在使用兩個 state 去管理,一個是存資料陣列 items,跟儲存選取資料的 selectedItem,然後在選取的時候去更新 selectedItem,並且用這個選取的資料去顯示我們要的畫面。但這會發生一個問題,當我們同時也想更新原本的 items 資料的時候,剛好更新到我們當下選取的 item,如果沒同時更新 selectedItem,我們畫面想呈現的內容可能就不會一起更新到。
要避免這樣的問題,我們可以把 selectedItem 改成只去存相關資料的 id,並且在每次 render 的時候透過 find() 這個方法去獲得選取資料:

const [items, setItems] = useState(initialItems)
const [selectedId, setSelectedId] = useState(initialItems[0].id)

const selectedItem = items.find(item => item.id === selectedId)

這樣不管 items 怎麼改裡面的內容,selectedItem 都能抓到剛好更新完的資料,因為他是透過改完的 items 來獲得的。這樣的設計方式就可以只保留到重要的 state,而把重複資料避免掉。

避免複雜的巢狀 state

之前就要提到,巢狀的 state 不管是更新或刪除都會十分複雜,讓我們開發處理的複雜度提高,所以我們在設計架構的時候會想要盡量避免這種情況發生。假設資料真的就是如此,官方文件也提供幾種方法讓我們參考:

  1. 讓資料扁平化(Flatten),透過把巢狀 state 改寫成類似樹(tree)的型態,把相關連的資料用 childIds 去管理。透過扁平化的處理,我們在更新 state 的時候就不用一層一層慢慢查找,而是只需要知道需要修改的資料 id 就好了。
  2. 把巢狀的資料當成 prop 傳到 child components 處理,顧名思義就是透過 props 的傳遞,讓資料在不同的 components 裡處理,這樣就能把 state 的複雜度降低。
  3. 使用 Immer,因為使用 Immer 就可以用一些 side effect 的寫法來更新,同時也可以確保效能。

小結

關於 State 架構,今天的介紹告一個段落,希望大家還好理解。在設計 state 的時候要考慮的面向很多,但我覺得一開始先盡量把想得到先宣告出來,之後再慢慢合併與刪減也沒關係,有時候就是需要累積點經驗,才能設計出最有效的 state。所以大家也不用太擔心一開始的 state 有沒有寫好,多試試看就知道,只是記得照著前面介紹的方法,來檢視自己的 state 設計是否還有更好的改良空間。
今天的文章就到這邊,感謝大家耐心地看完,如果有任何問題與建議歡迎告訴我,明天見,晚安。


上一篇
Day 23 - 用宣告式思考 UI
系列文
重溫 React 官方文件回到最初的起點24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言