Redux 是一個全域的資料管理工具,更簡單來說,
所有的資料都能放在 Redux,需要資料的 Component,也直接從 Redux 取得,所以
除此之外,如果我們在 Component 內寫下一堆關於獲取資料的 API 請求,那程式就會變得非常的亂,且發生問題時也不容易找到,而 Redux 在專案內的角色還能夠讓「畫面 Component」及「資料 Redux」分離,
最後更令人開心的是,在 Redux 新的版本 v7.1.0
中,也申裝上更符合 Hooks 的使用方法了!所以用起來會比去年更親民而且好上手!
說了那麼多,伸出你的手,擁抱 Redux 吧 :)
redux 是函式庫本身,react-redux 則是 Redux 與 React 的綁定使用套件。
npm install --save redux react-redux
安裝完後,接下來會一步一步說明 Redux 內的核心組成。
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 的工作就是在應用程式中負責整合所有的 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
是 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
是新版 react-redux 所增加的 Hooks,功能就像我們使用了 useContext
,只是換成 useSelector
而已:
相同道理 CurrentTask
也可以用相同的方式獲取 todoList
中的資料:
由於我們已經將 todolist
中的資料交給 Redux 保管, Component 內不需再使用 State 紀錄著相同的資料,留著也不會使用它,所以直接將 Main
中的 todolist
移除。
同樣的道理,因為 Main
裡面已經沒有 State 了,而且 TodoList
和 CurrentTask
只需透過 useSelect
就能獲取 todolist
的資料,也不需特別從 Main
中以 useContext
送給其他的 Component。
移除了 useState
和 useContext
後,程式碼大概會像這樣子:
本文的範例程式碼會提供在 GitHub 上,歡迎各位參考:)
關於最後提到的那段話:
真的很令人興奮,它使你能在處理畫面時不需要管任何關於資料的邏輯,處理資料邏輯時也不需要管畫面,更重要的是,
通常體會到這個真理時,都是表明著你在維護時對 Component 感到苦惱,你會在 Component 裡面的 Method 中跳躍著尋找 Bug 在哪,我不能說這是各位的必經之路,但至少就我而言,初期還滿常遇到這種狀況的,主要還是各位在遇到問題時,能試著去思考該怎麼解決,
如果文章中有任何問題,或是不理解的地方,都可以留言告訴我!謝謝大家!
Redux可以將資料隔離,聽起來很酷炫! 有種MVC的既視感 xDD。
不過還是想在這裡請問一下神Q有關於createContext和useSelector的差別,
使用createContext的情況下是因為仍舊把state放在組件當中,但是如果同時用這兩種方法,一個是Redux作管理,另一個是某元件裡面也有state管理的情況下,那同時使用這兩種方法會不會很奇怪。
我的想法是雖然可以統一管理資料,但是組件內或許存在一些較特殊或者小的資料是不需要讓Redux去管理的資料,再麻煩神Q大大解惑,QQ。