## 前言 - 在 `Redux` 中,有許多重複性高的模板code (boilerplate code)。像我們每次都要創建一個 `action` 物件、`action creator`、在 `reducer` 裡使用 switch case 來改變 `state` ...等。在專案中重複這些動作是很不方便的事。 - 使用 `Redux-toolkit` 可以讓開發變得更加有效率。 - 在這個章節會使用 `Redux-toolkit` 來實作前面蛋糕店、冰淇淋店的例子。 範例:https://codesandbox.io/p/sandbox/redux-6-redux-toolkit-yyy4r2 **備註:此篇範例是在 `node.js` 的環境跑** ## - step0. 專案 set up 1. 開啟一個新專案,並安裝 redux-toolkit ``` npm i @reduxjs/toolkit ``` 2. 資料夾結構 - `Redux-toolkit` 提供了一個建議的資料夾結構,以協助組織 `Redux` 相關的程式碼。 - 建議的資料夾結構通常是基於功能的(feature),而不是基於檔案類型的。 ``` /app - index.js /feautures /cake /iceCream ``` ## - step1. createSlice 說明: - 在 `redux-toolkit` 中,建議把一個功能的 `reducer` 邏輯和 `action` 放在一起,且檔案名稱以 `slice` 作為後綴。 - 把應用程式分成一個個 `slice` 來管理狀態。 - 我們在 `cake` 資料夾底下,新增 `cakeSlice.js` 引入 createSlice ```javascript=1 // features/cakes/cakeSlice.js // (因為是 node 環境,所以用 require) const createSlice = require("@reduxjs/toolkit").createSlice; ``` createSlice 接收一個物件,此物件包含三個參數: 1. slice的名字 2. 初始狀態 3. reducers ```javascript=1 // features/cakes/cakeSlice.js const cakeSlice = createSlice({ name: "cake", // slice的名字 initialState: { // 初始狀態 numOfCakes: 10, }, reducers: {} // reducers }); ``` ## - step2. reducers 內容 在前面不使用 `redux-toolkit` 時,我們用 `switch case` 來處理。 ```javascript=1 // features/cakes/cakeSlice.js const cakeReducer = (prevState = cakeState, action) => { switch (action.type) { case "CAKE_ORDER": const cakeQuantity = action.payload.quantity; return { ...prevState, cakes: prevState.cakes - cakeQuantity }; default: return prevState; } }; ``` 使用 `createSlice` 的 `reducer` 寫法: 1. 處理蛋糕下單,我們使用 order 做為 key,而 value 會是一個函式,此函式接收 `state` `action` 做為參數 2. 不同於之前的寫法,使用了 `createSlice`,我們可以直接改變 `state`,不用 return 新的狀態。 (`redux-toolkit` 使用了 [Immer](https://immerjs.github.io/immer/),因此我們可以直接改變狀態) ```javascript=1 // features/cakes/cakeSlice.js const cakeSlice = createSlice({ name: "cake", initialState: { numOfCakes: 10, }, reducers: { order: (state, action) => { state.numOfCakes -= action.payload.quantity; }, restock: (state, action) => { state.numOfCakes += action.payload.quantity; }, }, }); ``` ### 那麼 action 呢? 我們寫了 `reducers`,它會自動幫我們產生 action creator,名稱就和 `reducers` 裡定義的 key 相同 (order 和 restock)。 因此我們就不用再自己創建 action type 常數、action creator...等了。 ### export actions 和 reducers ```javascript=1 // features/cakes/cakeSlice.js module.exports = cakeSlice.reducer; module.exports.cakeActions = cakeSlice.actions; ``` ## - step3. 創建 store - 我們要在 `store.js` ,使用 configureStore 來創建 store - configureStore 接收一個物件,裡面有個 `reducer` 屬性,我們把 slices 的 reducer 設置在這邊。 ```javascript=1 // app/store.js const configureStore = require("@reduxjs/toolkit").configureStore; const cakeReducer = require("../features/cakes/cakeSlice"); const store = configureStore({ reducer: { cake: cakeReducer, }, }); module.exports = store; ``` ## - step4. 使用 store - 如同單純 `redux` 一樣,使用 `getState()` 來得到狀態;使用 `subscribe()` 來註冊。(詳見[[Redux] #2 Store, Action, Reudcer 基本寫法](https://hackmd.io/3vNH6QuVRNO7RGbgFl1MCA?view#Store)) - 使用 `dispatch()` 來發送 `action` - 直接使用 `redux-toolkit` 幫我們產生的 action creator ```javascript=1 // index.js const store = require("./app/store"); const cakeActions = require("./features/cakes/cakeSlice").cakeActions; // 取得狀態 console.log("Initial State:", store.getState()); // 偵測狀態改變 const unsubscribe = store.subscribe(() => { console.log("Updated State:", store.getState()); }); // 改變狀態(下單蛋糕,數量減3) store.dispatch( cakeActions.order({ quantity: 3, }), ); // 改變狀態(蛋糕補貨,數量加10) store.dispatch( cakeActions.restock({ quantity: 10, }), ); unsubscribe(); ``` console 結果如下 ``` Initial State: { cake: { numOfCakes: 10 } } Updated State: { cake: { numOfCakes: 7 } } Updated State: { cake: { numOfCakes: 17 } } ``` ## 總結 `redux-toolkit` 的使用方法 - 使用 `createSlice`,它會產生 action reducer - 可以直接改變 `state`,不用擔心 mutable 物件問題 - 使用 `configureStore` 來創建 `store` - `store.getState()` 取得狀態 - `store.subscribe()` 偵測狀態改變 - `store.dispatch()` 改變狀態 --- > 參考資料:https://www.youtube.com/watch?v=5EMyvYGzv0o&list=PLC3y8-rFHvwiaOAuTtVXittwybYIorRB3&index=20