Redux API 重點 - 分別是 Store、Actions、Reducer

前篇介紹了 Store 及 Actions,這篇就來詳細介紹 Reducer
Reducer 是用來應對改變 State 的函式,它是一個 Pure Function,接受兩個參數,先前的 State 和 Action。
當 Store 收到 Action 通知後,一定要傳回一個「全新的State」,這樣 React Component 才能發生變化。
(previousState, action) => newState
Store 接收到 Action 傳來的資料,然後根據邏輯計算資料,這個過程就稱為 Reducer。這個 Function 之所以被稱為 Reducer,是因為它就像是 Array.prototype.reduce(reducer, ?initialValue) 的 Pure Function 類型。
Pure Function- 將相同的輸入丟入,永遠都會回傳相同的輸出,並且不對任何該函數以外的任何作用域產生影響。
接下來我們可以假定 TODO MVC 為想要設計的應用程式,來思考 Reducer 要怎麼做設計

觀察 Todo MVC 的畫面,我們可以發現整個應用程式需要二個 State 如下,分別是 filter 字串,及 todos 陣列清單。
{
filter: 'ALL', // ACTIVE, COMPLETED
todos: [
{
text: '測試A',
completed: true,
},
{
text: '測試B',
completed: false
}
]
}
Reducer 是一個 Pure function,它接收先前的 State 和一個 action,然後回傳一個「全新的State」。
(previousState, action) => newState
首先,先定義一個 Initial State,如果傳入的 State 未定義,就設定Initial State給它,如果不是定義好的 Actions,都回傳先前的 State。
const initialState = {
filter: 'ALL',
todos: []
};
const todosReducer = (state = initialState, action) => {
switch (action.type) {
...
default:
return state
}
}
觀察 Todo MVC 的畫面操作,我們可以發現至少需要制定以下幾個 Action,且思考要回傳的全新的 State 的結構。
const todosReducer = (state = initialState, action) =>
switch (action.type) {
case SET_FILTER: // 依照 Filter 去顯示 TODO 項目
return Object.assign({}, state, {
filter: action.filter
});
case ADD_TODO: // 新增一個 TODO 項目
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
});
case TOGGLE_TODO: // 把一個 TODO 項目標示是否完成
return Object.assign({}, state, {
todos: state.todos.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
});
default: // 未知的 Action
return state;
}
}
使用 Object.assign 的方式,可確保永遠都是全新的 State,如果覺得這樣的操作很麻煩,也可以考慮引用第三方的函式庫,像是 Immutable.js 或是 Immer.js 來幫助開發。
仔細觀察 State,可以發現 todos 和 filter 的更新似乎是完全獨立的。操作 todos 及 filter 的 Action 對應,也可以拆分成二塊,所以我們可以拆分前面的 Reducer Function,變成二個 Reducer Function,且各自更新它們對應的 State。
const todosReducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false
}
];
case TOGGLE_TODO:
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
});
}
return todo;
});
default:
return state;
}
};
const filterReducer = (state = 'ALL', action) => {
switch (action.type) {
case SET_FILTER:
return action.filter;
default:
return state;
}
};
請記住這些 Reducer 每一個都管理它所擁有的全域 State 一部分。每個 Reducer 的 State 參數都不一樣,並對應到它管理的部分 State。
隨著應用程式大小增長,我們可以把 Reducers 拆分成個別的檔案並讓它們完全獨立並管理不同的資料領域。
拆分成各別區域狀態的 State,可稱之為 Slice (切片),所以對應的 Reducer 就稱為 Slice Reducer。未來介紹 Redux Toolkit 時會再出現一次此名詞。
Redux 提供一個 utility 叫做 combineReducers(),可以用來組合多個 Slice Reducer。
把組合好的 reducers 傳入到 createStore 函式中,就完成 Reducers 與 Store 的連結。
import {createStore, combineReducers} from 'redux';
const reducers = combineReducers(todosReducer, filterReducer);
cosnt store = createStore(reducers);
export default store;
在前面二篇,我們把 Redux API 的重點 Store、Actions、Reducer 都介紹完成了,也用範例來示範了建置的方式,接下來就要示範 React Compoent 要如何去使用 Redux 來完成狀態管理。
https://www.freecodecamp.org/news/what-is-redux-store-actions-reducers-explained/
https://www.laitimes.com/article/1od0a_1u849.html
https://chentsulin.github.io/redux/index.html
https://pjchender.dev/webdev/note-without-redux/
https://ithelp.ithome.com.tw/articles/10219962