這次的主題是板上已有非常多資源的Redux,但時代在變,Redux也在變,有很多做法已經不再適合現在主流 React functional component 的做法了,所以,本系列會以簡單介紹 Redux,包含基本概念與實作,還有官方推薦的Redux toolkit 的實作為主,內容取自於自己在工作上使用的經驗與網路上 youtube 的影片教學的內容集合濃縮,會偏向實際使用層面為主,在這系列文章當中你會實作 Redux 原生的 app,透過導入如何應用 Redux toolkit,和 React-redux 與 React 專案的結合。
Redux 本身是作為一個 Javascript 的狀態管理工具,所以不是一定只能和 React 做合體,一樣可以在各種不同框架下應用,在這個系列文章裡我將會示範純 Redux 是如何來做到 share 這些不同暫存的資料的。
他的概念和 React 本身的 useReducer hook 的概念很像,如果大家已經會用的話應該很快能夠映射這樣的管理工具,因為 useReducer hook 也是借鏡 Redux 而發展出來的 hook function,再透過 context provider 的原理做成 global state 以供專案內不同 components 互相引入使用。
如此你就能夠將畫面上的資料與UI的部分分離開來,以便後續的維護。
在大多數 SPA 架構下的專案中,一定會有需要做到 global state 的需求,那透過 redux 就能更有效地去管理所有的 global state,以 react 本身為例,你會需要透過 createContext 來定義要分享出來的 state 結構,再透過 provider 來決定分享出來的對象,但是數量一多,這部分的管理就需要優化,那 Redux 就能解決你的需要。Redux 提供的模式和工具更容易理解專案中的狀態何時、何地、為什麼以及如何更新,以及當這些更改發生時您的應用程序邏輯將如何表現。
讓我們回到 react 的 component 來解釋 Redux 是怎麼定義 Action的。
function Counter() {
// State: useState的基本,定義出state
const [counter, setCounter] = useState(0)
// Action: 這就是我們一般常見定義的state change function
const increment = () => {
setCounter(prevCounter => prevCounter + 1)
}
// View:
return (
<div>
Value: {counter} <button onClick={increment}>Increment</button>
</div>
)
}
透過這個圖表應該更能夠清楚解釋上面 react code 的邏輯
這樣簡單的模組看似上沒有什麼問題,但當你專案的 state 開始變多又要分享給其他的 component 去使用的時候,這樣的結構就無法負荷。解決這個問題的一種方法是從 component 中提取共享的 state,並將其放在 component 組合之外的集中位置 (ex: context provider + useReducer hook)。如此,我們的 component 組合就會只剩下 View 的部分,任何 component 都可以訪問 state 或觸發 Action,簡單來說就是將 State & Action 的部分給拆出來,透過引入的方式去處理 State,這樣能更有效的利用每個 state。
const addTodoAction = {
type: 'todos/todoAdded',
payload: 'Buy milk'
}
// 或是採用function
const addTodo = text => {
return {
type: 'todos/todoAdded',
payload: text
}
}
Reducers:
這裡的作法基本上就和官方的 useReducer hook 是一樣的,可以依據不同的 action type 去處理不同的 state change function。
此外有幾個原則必須注意:
// sample from redux office doc
const initialState = { value: 0 }
function counterReducer(state = initialState, action) {
// 檢查核對actio.type的state change function
if (action.type === 'counter/increment') {
// 如果有, make a copy of `state`
return {
...state,
// and update the copy with the new value
value: state.value + 1
}
}
// otherwise return the existing state unchanged
return state
}
// sample code from redux doc
import { configureStore } from '@reduxjs/toolkit'
const store = configureStore({ reducer: counterReducer })
console.log(store.getState())
// {value: 0}
store.dispatch()
,並且傳入 action object 做為參數,接著他會去觸發 reducer 內所定義好的 function 並更新 state。// sample code from redux doc
store.dispatch({ type: 'counter/increment' })
console.log(store.getState())
// {value: 1}
// sample code from redux doc
const selectCounterValue = state => state.value
const currentValue = selectCounterValue(store.getState())
console.log(currentValue)
// 2
以下為 Redux data Flow 的示意圖,這個圖片出處為 redux 官方文件內的動畫,已非常清楚的解釋了上述各個關鍵字,在整個資料流裡面分別扮演的角色。
那麼今天的內容就先到這裡,明天我們來實際使用 redux 做一個新的專案吧!
參考文獻:
Redux官方