[實作]使用 Redux 實作 Todo-List
Redux 基礎概念
Redux 的首頁就提到:Redux is a predictable state container for JavaScript apps
,這句話指出Redux希望能夠提供一個容器,這個容器可以用來管理以及預測 State,讓開發這能夠在開發複雜的 Javascript app 下,對於資料狀態的控制能夠更加的簡便以及直覺。
P.S.:Redux的名字看起來好像與React有相關性,但是兩這之間並沒有相依性,Redux可以用在任何JavaScript框架,或是不用任何JavaScript框架。
Redux 資料流
- ACTION:使用者透過觸發 ACTION ,來改變 STORE 中 STATE的狀態,一個 ACTION 通常包含 TYPE 和 PAYLOAD。
- REDUCER:REDUCER 會根據 ACTION 的 TYPE 去執行對應到的行為來改變 STORE 中 STATE 的狀態。
- STORE:將整個 JavaScript APP 的 STATE 集中起來進行管理以及儲存的容器,會將不同 STATE 用Object Tree的方式儲存起來。
- VIEW:當 STORE 中的 STATE 有改變的時候,會更新對應到的畫面(VIEW)。
當使用者跟 VIEW 有互動的時候,會觸發了事件而發送 ACTION 到 REDUCER,REDUCER 會根據 ACTION 中的 TYPE 去對應到相對的行為然後回傳新的 STATE 到 STORE,而 VIEW 發現 STORE 中的 STATE 有改變的時候,就會從新繪製畫面。
實作 Todo List
首先,先用我們擅長用的 create-react-app 幫我們建立一個 React 專案,此時並沒有包含 Redux ,所以我們需要另外安裝它:
npm install --save react-redux
安裝完 react-redux 之後我們在 src 的資料夾下面,新增下列的資料夾,事實上我們不見得需要新增這些資料夾,多了這些資料夾會讓我們在 coding 的時候,容易管理程式碼的位置,以及維護的方便性。
- actions → 在 actions 資料夾中會放入,當使用者觸發了畫面事件時,會執行哪些行為,像是輸入文字。
- components → 設計好各個不同的 components。
- constants → 可以存放一些常數,常見會在裡面放進不同 action 的行為常數;例如:『新增代辦事件』此行為就會以
ADD_TODO
來當作是這個行為的常數。 - reducers → 當有使用者行為觸發了事件之後,我們需要根據事件的 type 來去執行相對應的動作,接者改變 store 內的 state。
- store → 會將所有我們需要用到的 reducer 進行連結,打包成一個儲存 state 跟 action 的地方。
首先,我們先分析一個基本的 TodoList ,使用者在使用得時候會觸發哪些行為 。
分別有新增Todo
、完成Todo
、刪除Todo
以及輸入Todo Title
,我們根據這四個行為,在 action-type.tsx
中加入四個相對應的常數。
接者,我們可以開始設計 Store 中的 State 了。首先,我們到 Reducers 中新增 todoReducer.tsx
。
todoReducer.tsx
分為兩個部分:
- initialState:為初始的 State 狀態,基本上跟在 component 裡設計 State 是一樣的像法;這個例子中,State 中有
todos
以及input
,todos
是一個陣列,存放所有代辦事項,input
是當輸入欄有更動時會改變 title 的文字。 - todoReducer:當中包含兩個參數需要輸入,
state
以及action
,state
是這個 reducer 的 State,action
包含了這個行為的 type 以及 payload ; reducer 會根據action.type
去執行相對應的行為,並且會根據action.payload
來改變state
的內容,而當state
改變時,畫面就會從新渲染了。
我們設計完 todoReducer
後,就要把 reducer 連接到 store
中,在 redux 中一個JavaScript App 只能有一個store
,但是我們在設計 APP 的時候,可能會有很多不同的 State,如果將這些 State 都寫在一個 reducer 中,會導致 reducer 中的程式碼很混亂,為了避免這個情況,我們可以根據 state 的功能來進行分類,之後在全部綁定成一個 rootReducer
中,儲存在store
中就可以了。
我們可以將所有設計出來的 reducers ,透過 redux 的 combineReducers
集中綁定到 rootReducer
中後,在用 createStore
將 rootReducer
儲存到唯一的 store
中。
到這邊,大家可能會有疑惑說, reducer
的 action
參數是從哪裡來的。當我們在畫面上有行為產生,同時觸發了 action
,就是上述說的參數。
所以我們在剩下的資料夾 actions
新增 todoAction.tsx
, todoAction.tsx
裡面會描述有有關於相對的 reducer 的行為,回傳一個包含 action.type
以及 action.payload
的結構。
上面程式碼可以看到我們針對了兩個行為, ADD_TODO
以及FINISH_TODO
做了兩個 action,最後分別回傳了一個物件,包含 type
以及payload(載體)
, type
就是 action 的常數, payload
則是要運送到 reducer
中要去做計算的負載物。
做完上述的動作後,我們就搞定我們的 action
、 reducer
以及 store
了,剩下就是讓我們的 TodoList 可以使用這些東西。
我們在 App.tsx
引入 store
,用 redux
的 Provider
將 store
載入我們的 TodoList 中。之後,我們就只要在需要用到 store
的 component ,去連接需要的 state 以及 action 即可。
mapStateToProps
將store
中提出需要用到的state
。mapDispatchToProps
會把action
綁定在元件相對應的事件上 ,例如:onInput
會在輸入欄有變化的時候觸發,接者就會執行changeInput
,接者回傳action
物件給reducer
進行處理。
最後,將 mapStateToProps
以及 mapDispatchToProps
用 redux
提供的API connect
在元件上。
總結
到這個步驟,就完成了最基本的 Redux 設定了,接著就是熟能生巧,多用就能夠多熟悉 Redux 了。對於Redux 中處理非同步問題有興趣的話,可以去 Redux官網提到的 middlewave
去看看非同步問題是怎麼運作的。
最後,各位如果對文章內容有任何建議或指正,歡迎留言指教。
上述 TodoList 所使用的程式碼範例:https://github.com/tony14256/React-Redux-TodoList