[實作]使用 Redux 實作 Todo-List

Jian-Kai,Kuo
LT Lab
Published in
8 min readAug 10, 2019
圖片來源:https://redux.js.org/

Redux 基礎概念

Redux 的首頁就提到:Redux is a predictable state container for JavaScript apps,這句話指出Redux希望能夠提供一個容器,這個容器可以用來管理以及預測 State,讓開發這能夠在開發複雜的 Javascript app 下,對於資料狀態的控制能夠更加的簡便以及直覺。

P.S.:Redux的名字看起來好像與React有相關性,但是兩這之間並沒有相依性,Redux可以用在任何JavaScript框架,或是不用任何JavaScript框架。

Redux 資料流

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 ,使用者在使用得時候會觸發哪些行為 。

TodoList 畫面

分別有新增Todo完成Todo刪除Todo 以及輸入Todo Title ,我們根據這四個行為,在 action-type.tsx 中加入四個相對應的常數。

src>contants>action-type.tsx

接者,我們可以開始設計 Store 中的 State 了。首先,我們到 Reducers 中新增 todoReducer.tsx

src>reducers>todoReducer.tsx

todoReducer.tsx 分為兩個部分:

  • initialState:為初始的 State 狀態,基本上跟在 component 裡設計 State 是一樣的像法;這個例子中,State 中有 todos 以及inputtodos 是一個陣列,存放所有代辦事項, input 是當輸入欄有更動時會改變 title 的文字。
  • todoReducer:當中包含兩個參數需要輸入, state 以及 actionstate 是這個 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 中就可以了。

src>reducers>index.tsx
src>store>index.tsx

我們可以將所有設計出來的 reducers ,透過 redux 的 combineReducers 集中綁定到 rootReducer 中後,在用 createStorerootReducer 儲存到唯一的 store 中。

到這邊,大家可能會有疑惑說, reduceraction 參數是從哪裡來的。當我們在畫面上有行為產生,同時觸發了 action ,就是上述說的參數。

所以我們在剩下的資料夾 actions 新增 todoAction.tsxtodoAction.tsx 裡面會描述有有關於相對的 reducer 的行為,回傳一個包含 action.type 以及 action.payload 的結構。

src>actions>todoAction.tsx

上面程式碼可以看到我們針對了兩個行為, ADD_TODO 以及FINISH_TODO 做了兩個 action,最後分別回傳了一個物件,包含 type 以及payload(載體)type 就是 action 的常數, payload 則是要運送到 reducer 中要去做計算的負載物。

做完上述的動作後,我們就搞定我們的 actionreducer 以及 store 了,剩下就是讓我們的 TodoList 可以使用這些東西。

src>App.tsx

我們在 App.tsx 引入 store ,用 reduxProviderstore 載入我們的 TodoList 中。之後,我們就只要在需要用到 store的 component ,去連接需要的 state 以及 action 即可。

  • mapStateToPropsstore 中提出需要用到的 state
  • mapDispatchToProps 會把 action 綁定在元件相對應的事件上 ,例如: onInput 會在輸入欄有變化的時候觸發,接者就會執行 changeInput ,接者回傳 action 物件給 reducer 進行處理。

最後,將 mapStateToProps 以及 mapDispatchToPropsredux 提供的API connect 在元件上。

--

--