這篇主要會講述 Redux 的一些介紹,下篇才會開始進行實作。
Redux 是什麼?
Redux 是專門管理 state 的函式庫,它可以將整個專案的 state,儲存在一個樹狀物件並放在唯一的 store 裡面。
為何需要 Redux?
方便管理 state。沒有使用 Redux 的話,我們要透過元件將 state 進行傳遞,如果有 Redux 就可以將 state 集中管理,有需要時再從 store 傳遞給元件。
另外透過 Redux 也可以將操作元件 state 的邏輯部分從元件抽離出來,達到元件做純渲染,Redux 處理資料邏輯的結果。
特色
- 不一定要和 React 一起使用,和 React 一起使用的話需要使用 react-redux 將兩者銜接起來,透過 react-redux 可以將 store 內的 state 注入到元件內
- Redux 架構圍繞著嚴格的單向資料流。(可參考下圖)
圖片來源: Redux 官網
- Redux 主要由 action、reducer、store 等三個部分構成,以下詳細說明它們的作用&整個 redux 運作的過程圖
圖片截自 Udemy 線上課程 Modern React with Redux
action creator
當某個事件發生時,會建立一個 JS 物件,該物件即為 action。
action
action 裡面有 type 和 payload 屬性,一定要有 type 屬性,payload 則非必要,分別描述了行為的類型和要傳遞的 state。
dispatch
將 action 傳遞到 reducer 函式。
reducer
reducer 是一個 pure function,它會取得先前的 state 和一個 action,並根據傳入的 action 的 type 去將 state 值做改變,最後回傳的是一個經過計算後新的 state 物件。
另外在一個專案中可以有多個 reducer,每一個都管理它所擁有的全域 state 一部分。
使用 reducer 注意的點:
- 不能回傳 undefined(或沒有回傳值),其他值則都可以
- 呼叫 reducer 時會傳入兩個參數: 之前產生的 state 和傳入的 action
-
reducer 是純粹函式(不得執行有副作用的程式碼,ex: 呼叫 api,呼叫非純粹的函式),它接收先前的 state 和一個 action,回傳下一個 state
常見的 side effects 包括:
- Logging a value to the console
- Saving a file
- Setting an async timer
- Making an AJAX HTTP request
- Modifying some state that exists outside of a function, or mutating arguments to a function
- Generating random numbers or unique random IDs (such as Math.random() or Date.now())
- 要用 immutable 的方式對陣列和物件進行變更,以下是錯誤示範
以下的方式能正確的對陣列和物件進行變更:
如附圖,淡綠色的框框是正確修正 state 的方式,共通點是利用回傳新的陣列/物件來修正 state,其中物件的屬性移除最好用_.omit
方法,而不是其上面的方式,因為會產生空的鍵值對(例如{ name: undefined }
)
store
reducer 回傳後的 state 放在這邊做管理,讓由 react 或其他語言製成的應用程式可以取用。
Redux 的 best practice
這個段落紀錄 Redux 官網 Redux Style Guide 的部分內容,告訴讀者怎麼樣寫 Redux 會是比較好的做法,總共分三種重要等級: 必要、強烈推薦、推薦,以下會分別介紹。
必要
- 不要 mutate state
- reducers 內不能有副作用(AJAX、timeouts、非同步處理、Date.now(), Math.random())
- 不要將 Non-Serializable 值(Promises、Symbols、Maps/Sets、functions、class instances) 儲存在 state 裡,試想這些要更新值時怎麼處理?
- 一個應用程式只會有一個 redux store
強烈推薦
- 使用 Redux toolkit 去取代單純的 redux 寫法,更有效率更簡潔
- 使用 immer 去更新 state 值
- 根據 feature 去拆分資料夾結構,例如以前的 redux 有些會用 actions/reducers/types 去拆分,現在推薦根據將同個功能相關的 redux 程式碼寫在同個資料夾結構下
- 盡量將邏輯寫在 reducers 裡面,讓元件更純粹的渲染 UI,也更好 debug
- reducer 命名
import usersReducer from 'features/users/usersSlice'
import postsReducer from 'features/posts/postsSlice'
const rootReducer = combineReducers({
// usersReducer, // 不推薦
users: usersReducer,
posts: postsReducer
})
- 用 Normal State 的格式去儲存 state
- 只儲存最必要的 state 在 store 內,像有些過濾過的 state,需要用時再額外處理,再搭配 memorized 函式庫去暫存
- 把 actions 視為一個事件,而非 setter 函式
- 避免去依序的觸發多個 actions
推薦
- 使用
domain/eventName
格式寫 action type
- 使用 RTK Query 呼叫 api
- 避免將表單 state 存入 store
2023/07/03 補充-為什麼 redux 更新 state 要用 immutable 的方式更新?
這和 redux 在做 state 更新的對比機制有關,redux 做的是去確認更新前後兩個 state 的 reference 是否相同,而不是一層層屬性的去比對兩個物件型別的每個屬性值是否相同。
這麼做的原因是和效能有關,比對兩個物件的 reference 是否相同,會比較單純且快速,並且在 reducers 裡面使用 pure function 更新 state 減少 side effect,可以幫助我們更好 debug 避免更新到不對的值。
在對 Redux 有初步的認識後,明天將會實作一個範例。