Redux
本篇為 [FE303] React 的好夥伴:Redux 這門課程的學習筆記。如有錯誤歡迎指正!
根據 Redux 官網說明,可知 Redux 是一個「用於 JavaScript 應用程式的狀態管理工具」:
A Predictable State Container for JS Apps
雖然 Redux 經常與 React 搭配使用,但其實 Redux 是一種前端的「架構模式」。簡單來說,就是用來實現「狀態管理機制」的套件,並且能套用在任何程式語言。
再進一步認識 Redux 之前,可先從 Redux 的歷史演化來瞭解。
最早是在 Facebook 提出 React 時,所提出的 Flux 開發架構,目的是解決 MVC 在大型商業網站所存在的問題,例如管理 UI 畫面與資料之間的對應關係。
從以前到現在,關於「狀態管理」,在不同框架也會有不同處理方式,舉例來說:
以 Vue.js 為例,是透過 v-model
語法進行雙向綁定:
根據 Flux 官網說明:
Application architecture for building user interfaces
在傳統的 MVC 架構,Model 和 View 之間可能會呈現複雜關係:
(圖片來源:Facebook 介紹影片)
Facebook 當初之所以會提出 React 和 Flux,就是為了解決當 APP 架構變更大,功能變更複雜時遇到的問題。例如 FB 中的 Messager 的提醒通知,程式碼中可能有許多部分會操控到同一 Model,使得狀態過於複雜,不易進行後續追蹤。
Flux 架構流程如下,若想要改變 Store(資料)或 View(畫面),都必須透過 Dispatcher 發出 Action(指令),呈現單向資料流:
上述架構,在小型專案中其實是多此一舉,直接透過修改 Store 去渲染 View 即可;但在大型專案中,透過像這樣「集中」管理的方式,會更容易進行複雜的狀態管理。
以下是 Redux 中的 data flow 示意圖:
在 React 當中,其實有個和 Redux 功能類似的內建 Hook:useReducer,同樣可用來管理複雜的狀態。
useReducer 可接受三個參數:
以下是官網提供的範例:
透過上述簡介,我們可以瞭解到 React 和 Redux 其實並無相依性,兩者均可進行狀態管理,差別在於:
首先依照官方文件安裝 Redux:
官方文件是使用 import,需注意在 node.js 較舊版本無法使用,因此這裡使用 require。
透過以下程式碼,可創建 Redux 中的 Store:
在終端機執行 node app.js
,可知 store 其實是一個物件:
若改城呼叫物件裡面的函式 getState()
,會顯示目前的 state,也就是 initialState:
接著可透過 store.dispatch() 指定要做的事,傳入參數為物件,慣例寫法是 type: '...'
:
結果如下:
{ type: '@@redux/INIT0.1.0.m.r.p' }
:初始化時 redux 自動建立的 dispatch{ type: 'plus' }
:印出 dispatch 的 action也可以透過 setTimeout()
驗證:
結果如下,1 秒後 reducer 才印出傳入的 action:
改寫如下,傳入 action 會改變 state:
印出結果:
store 其實也有提供類似 addEventLister 的功能,也就是 subscribe(),傳入一個 function 作為參數:
再改寫上述範例:
結果如下:
以實作新增和刪除功能為例:
和 React 的 useState 用法類似,因為 reducer 回傳新的 state 會直接覆蓋原有的,必須加上 ...state
保存原本的 state:
新增 delete_todo 這個 dispatch:
並在 reducer 新增條件,寫入要執行的動作:
結果如下:
像這樣分開撰寫的好處,就是方便對 reducer 寫測試,可藉由比對結果,驗證邏輯是否正確,如以下範例:
當程式變複雜時,我們可透過「action constants」和「action creator」進行管理,可減少開發時出錯:
在上述範例中,我們是以「字串」來表示 type,但這麼做有個缺點,就是打錯字或發生錯誤時不易 debug,可透過 ActionTypes 物件集中管理:
將原本的字串改用 ActionTypes 物件表示: