在一般的專案開發中,功能當然不會只有一個 counter 這麼簡單,一個基本的網站可能就包含登入、列表等功能,而登入的狀態是很典型會被應用在許多不同 component 的資料 (app-wide state),接下來就會探討如何在 Redux 中管理多項不同的功能
延續上一章的 Counter 功能,接著要在專案中加入登入的功能,可能會有一組新的 component 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { Fragment } from "react";
import Counter from "./components/Counter"; import Auth from "./components/Auth"; import Header from "./components/Header";
function App() { return ( <Fragment> <Header /> <Auth /> <Counter /> </Fragment> ); }
export default App;
|
接著在 Redux store 中加入登入的功能,以技術上來說,我們完全可以在已存在的 counterSlice 中加入登入相關的 state & action,但在開發時,為了更好的維護專案,我們盡可能還是將不同的功能加以區分,因此額外建立一個 authSlice 是更好的解決方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const counterSlice = createSlice({});
const initialAuthState = { isAuthenticated: false, };
const authSlice = createSlice({ name: 'auth', initialState: initialAuthState, reducers: { login(state) { state.isAuthenticated = true; }, logout(state) { state.isAuthenticated = false; }, } })
|
但這裡要注意的是,無論我們因應功能需求增加了多少 slice,仍舊只會有一個 store,store 有只會有一個 reducer,因此如果出現多個 slice 的情況,就要將 reducer 改成 object,其中每一個 key-value 就代表一組 slice
1 2 3 4
| const store = configureStore({ reducer: { counter: counterSlice.reducer, auth: authSlice.reducer }, });
|
actions 則可以個別 export
1 2
| export const counterActions = counterSlice.actions; export const authActions = authSlice.actions;
|
而在 component 的使用上有需要些許的調整,在使用 state 時,需要特別指明是哪一組 slice,名稱就來自 store reducer 中自定義的 key
1 2 3 4 5
| const Counter = () => { const dispatch = useDispatch(); const counter = useSelector((state) => state.counter.counter); const show = useSelector((state) => state.counter.showCounter); };
|
隨著開發的功能越來越豐富,如果都將所有 Slice 寫在 store/index.js 內,這個檔案就會變得肥大難以維護,所以在開發時通常會將每個 Slice 以單一檔案個別管理,再統一匯入 store/index.js 建立 store
在建立 store 時,其實只需要 Slice.reducer,所以每個 Slice export 可以只提供 Slice.reducer;另外,Actions 則由每個 Slice 檔案個別 export
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import { createSlice } from "@reduxjs/toolkit";
const initialState = { counter: 0, showCounter: true, };
const counterSlice = createSlice({ name: "counter", initialState, reducers: { ... }, });
export const counterActions = counterSlice.actions; export default counterSlice.reducer;
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counter"; import authReducer from "./auth";
const store = configureStore({ reducer: { counter: counterReducer, auth: authReducer }, });
export default store;
|
Component Import
在 Component 的使用上,因為 Actions 改由個別 Slice export,所以當然也需要改從 Slice 檔案 import
1 2 3 4 5 6 7 8
|
import { useSelector, useDispatch } from 'react-redux'; import { counterActions } from '../store/counter';
const Counter = () => { ... };
|
資料參考
React - The Complete Guide (Incl Hooks, React Router, Redux)