iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
3
Modern Web

在 React 生態圈內打滾的一年 feat. TypeScript系列 第 13

Day12 | React 的快樂小夥伴 - Redux 資料管理篇

  • 分享至 

  • twitterImage
  •  

前言

Redux 是一個全域的資料管理工具,更簡單來說,

就是能替你管理整個網站需要的 State,

所有的資料都能放在 Redux,需要資料的 Component,也直接從 Redux 取得,所以

組件管理 state 變得更方便,而且也確保整個專案的資料都來自同一個地方。

除此之外,如果我們在 Component 內寫下一堆關於獲取資料的 API 請求,那程式就會變得非常的亂,且發生問題時也不容易找到,而 Redux 在專案內的角色還能夠讓「畫面 Component」及「資料 Redux」分離,

有關畫面的就修改 React 的 component,資料的部分就完全交給 Redux,每個人各司其職、分工合作。

最後更令人開心的是,在 Redux 新的版本 v7.1.0 中,也申裝上更符合 Hooks 的使用方法了!所以用起來會比去年更親民而且好上手!

說了那麼多,伸出你的手,擁抱 Redux 吧 :)


前置準備

  1. 文中的專案會以 Day11 的專案架構繼續講解,如果未跟到前一天的進度,可以從 GitHub 上 Clone 下來。
  2. 一顆擁有學習熱忱的心。

使用方法

安裝 Redux

redux 是函式庫本身,react-redux 則是 Redux 與 React 的綁定使用套件。

npm install --save redux react-redux

安裝完後,接下來會一步一步說明 Redux 內的核心組成。

Reducer

Reducer 在 Redux 中是用來保管 state,以及在接收到不同的 action 指令時該對 state 做什麼動作的函數。

先打開 clone 下來的專案,並在 ./src 目錄下建立一個 reducer 的資料夾,並在裡面新增一個檔案叫做 todolist.js,像這樣:

|- src
 |-reducer
  |-todolist.js

接著打開 .src/reducer/todolist.js,首先要為該 Reducer 設計它所管理的 State 架構,下方沿用上一篇的 todolist 的 State 格式:

const initState = {
  todoList: ['第一件事情', '第二件事情'],
};

在 initState 中另外將資料架構清楚列出,能讓接手的人或是兩個禮拜後的自己,一看就能知道這個 Reducer 保管了哪些資料,且預設了初始資料的格式,也比較不會讓 Component 得到錯誤的 State 格式而 Render 失敗。

現在有了初始資料,就可以使用它建立一個 Reducer:

const todoReducer = (state = initState, action) => {
  switch (action.type) {
    default:
      return state;
  }
};

export default todoReducer;

每一個 Reducer 都會有兩個參數,第一個參數會將初始的資料狀態 initState 交由 Reducer 保管,第二個參數會傳入現在 Reducer 要對 State 做什麼動作的指令及額外的參數,這些在明天會接著講解。

所以在還沒有任何 action 指令描述的 Reducer 內,預設回傳了它所保管的 State ,在這裡就是上方的 initState

記得要將 Reducer 給 export,否則在下一步創建 Store 的時候會沒辦法 import。

Store

Store 的工作就是在應用程式中負責整合所有的 Reducer,這裡我會在 src 中為 Store 創建屬於它的目錄,如下:

|- src
 |-store
  |-index.js

接著打開 src/store/index.js,因為我們只有一個 Reducer,所以直接 import todoReducer,並將它提供給 createStore 創建一個 Store 就行了,最後要記得將 Store export 出去:

import { createStore } from 'redux';
import todoReducer from '../reducer/todolist';

const store = createStore(todoReducer);

export default store;

如果有多個 Reducer 的情況,就得使用 combileReducer 先將 Reducer 給組合起來,再將組合後的結果送給 createStore 創建 Store:

import { createStore, combineReducers } from 'redux';
import todoReducer from '../reducer/todolist';
import otherReducer from '../reducer/other';

const rootReducer = combineReducers({
  todoReducer,
  otherReducer,
});

const store = createStore(rootReducer);

Provider

Provider 是 react-redux 中的組件,它會接收上方創建的 Store,記得上一篇的 createContext 創建出來的 Component 嗎?其實 Provider 在做的事情就和它一樣,在 Component 的最上層用 Store 管理所有 Reducer ,而 Reducer 則是管理 State。

Provider 設置的位置會在整個 Project 的最上層,也就是各位最後使用 ReactDOM.render 指定的那個 Component,以當前手上的專案來說 ReactDOM.render 位於 src/index.js 裡面,而被它指定 Render 的就是 Main,因此在這裡 Main 就是專案裡最上層的 Component,而 Provider 就得包著它,使用的同時也必須提供 store 的 Props 給 Provider

useSelector

useSelector 是新版 react-redux 所增加的 Hooks,功能就像我們使用了 useContext,只是換成 useSelector 而已:

相同道理 CurrentTask 也可以用相同的方式獲取 todoList 中的資料:

移除 State 及 createContext

由於我們已經將 todolist 中的資料交給 Redux 保管, Component 內不需再使用 State 紀錄著相同的資料,留著也不會使用它,所以直接將 Main 中的 todolist 移除。

同樣的道理,因為 Main 裡面已經沒有 State 了,而且 TodoListCurrentTask 只需透過 useSelect 就能獲取 todolist 的資料,也不需特別從 Main 中以 useContext 送給其他的 Component。

移除了 useStateuseContext 後,程式碼大概會像這樣子:

看!Component 的內容變得單純了,因為我們將資料層抽離在 Redux 中, Component 就只需要向 Redux 取資料,然後負責畫面的呈現。

本文的範例程式碼會提供在 GitHub 上,歡迎各位參考:)


結尾

關於最後提到的那段話:

「將資料隔離在 Redux,讓 Component 只處理畫面。」

真的很令人興奮,它使你能在處理畫面時不需要管任何關於資料的邏輯,處理資料邏輯時也不需要管畫面,更重要的是,

將畫面和資料區分出來,便不會使得 Component 內隨著專案越來越大而變得一團糟!

通常體會到這個真理時,都是表明著你在維護時對 Component 感到苦惱,你會在 Component 裡面的 Method 中跳躍著尋找 Bug 在哪,我不能說這是各位的必經之路,但至少就我而言,初期還滿常遇到這種狀況的,主要還是各位在遇到問題時,能試著去思考該怎麼解決,

並且想到就寫!即使它是錯的,但只要你不停止思考,便能在一次次的錯誤中找到最棒的設計模式!

如果文章中有任何問題,或是不理解的地方,都可以留言告訴我!謝謝大家!


上一篇
Day11 | 你有傳送門,我有 useContext!
下一篇
Day13 | React 的快樂小夥伴 - Redux 事件處理篇
系列文
在 React 生態圈內打滾的一年 feat. TypeScript31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
matuyou0301
iT邦新手 5 級 ‧ 2021-04-15 12:48:10

Redux可以將資料隔離,聽起來很酷炫! 有種MVC的既視感 xDD。

不過還是想在這裡請問一下神Q有關於createContext和useSelector的差別,
使用createContext的情況下是因為仍舊把state放在組件當中,但是如果同時用這兩種方法,一個是Redux作管理,另一個是某元件裡面也有state管理的情況下,那同時使用這兩種方法會不會很奇怪。

我的想法是雖然可以統一管理資料,但是組件內或許存在一些較特殊或者小的資料是不需要讓Redux去管理的資料,再麻煩神Q大大解惑,QQ。

我要留言

立即登入留言