iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
自我挑戰組

30天深入淺出Redux系列 第 9

Redux 深入淺出 - [ Day 9 ] Reducer 的拆分

  • 分享至 

  • xImage
  •  

回首我們目前的功能已經相對完善許多,但站在維護的角度,我們應該可以做得更好一些!

程式語言畢竟還是語言,既然是語言,我們能做的就是讓其更加言之有物,這樣也比較方便後續的維護,依照這樣的思維,我們再回到我們已經完成的 orderReducer 去看,是不是會覺得這個檔案和我們賦予他的名字對不太上啊,憑什麼要 order 去處理 restock 的工作勒,作為一名資深社畜肯定是不幹的,所以寫程式也得講究個名正言順!

https://ithelp.ithome.com.tw/upload/images/20220908/20129020synMXNFWHF.png

那我們就將原本在 initialState 的 key 都獨立開來,讓他們各自爲政,來模擬一下之後專案複雜化的情境,以 numOfCoffee 為例,我們重新新增一個 reducer 檔案叫 coffeeReducer.js,那他的工作只需要控制咖啡的數量就好,如以下:

// coffeeReducer.js
const { COFFEE_ORDERED, COFFEE_RESTOCKED } = require("../action/types")

const initialState = {
  numOfCoffee: 20
}

const coffeeReducer = (state = initialState, action) => {
  switch (action.type) {
    case COFFEE_ORDERED:
      return {
        ...state,
        numOfCoffee: state.numOfCoffee - action.payload.qty,
      }
    case COFFEE_RESTOCKED:
      return {
        ...state,
        numOfCoffee: state.numOfCoffee + action.payload.qty
      }
    default:
      return state;
  }
}

module.exports = { coffeeReducer }

一樣的概念我們將咖啡豆也拆出來,如下:

// coffeeBeanReducer.js
const { COFFEEBEAN_ORDERED, COFFEEBEAN_RESTOCKED } = require("../action/types")

const initialState = {
  numOfCoffeeBean: 20
}

const coffeeBeanReducer = (state = initialState, action) => {
  switch (action.type) {
    case COFFEEBEAN_ORDERED:
      // 顧客買 => 商品-, 營收+ 
      return {
        ...state,
        numOfCoffeeBean: state.numOfCoffeeBean - action.payload.qty
      }
    case COFFEEBEAN_RESTOCKED:
      // 補貨 => 商品+, 營收-
      return {
        ...state,
        numOfCoffeeBean: state.numOfCoffeeBean + action.payload.qty
      }
    default:
      return state;
  }
}

module.exports = { coffeeBeanReducer }

接著處理蛋糕的部分:

// cakeReducer.js
const { CAKE_ORDERED, CAKE_RESTOCKED } = require("../action/types")

const initialState = {
  numOfCake: 20
}

const cakeReducer = (state = initialState, action) => {
  switch (action.type) {
    case CAKE_ORDERED:
      // 顧客買 => 商品-, 營收+ 
      return {
        ...state,
        numOfCake: state.numOfCake - action.payload.qty,
      }
    case CAKE_RESTOCKED:
      // 補貨 => 商品+, 營收-
      return {
        ...state,
        numOfCake: state.numOfCake + action.payload.qty,
      }
    default: 
      return state;
  }
}

module.exports = { cakeReducer }

最後,還有營收的部分,這邊不管哪個動作都跟營收有關,所以他的 case 也會很多,我們姑且取名為 assetsReducer.js,如下:

// assetsReducer.js
const { COFFEE_ORDERED, COFFEE_RESTOCKED, COFFEEBEAN_ORDERED, COFFEEBEAN_RESTOCKED, CAKE_ORDERED, CAKE_RESTOCKED } = require("../action/types");

const initialState = {
  assets: 1000,
}
// 這部分和useReducer hook是一樣的
const assetsReducer = (state = initialState, action) => {
  switch(action.type) {
    case COFFEE_ORDERED:
      // 顧客買 => 商品-, 營收+ 
      return {
        ...state,
        assets: state.assets + action.payload.income
      }
    case COFFEE_RESTOCKED:
      // 補貨 => 商品+, 營收-
      return {
        ...state,
        assets: state.assets - action.payload.pay
      }
    case COFFEEBEAN_ORDERED:
      // 顧客買 => 商品-, 營收+ 
      return {
        ...state,
        assets: state.assets + action.payload.income
      }
    case COFFEEBEAN_RESTOCKED:
      // 補貨 => 商品+, 營收-
      return {
        ...state,
        assets: state.assets - action.payload.pay
      }
    case CAKE_ORDERED:
      // 顧客買 => 商品-, 營收+ 
      return {
        ...state,
        assets: state.assets + action.payload.income
      }
    case CAKE_RESTOCKED:
      // 補貨 => 商品+, 營收-
      return {
        ...state,
        assets: state.assets - action.payload.pay
      }
    default: 
      return state;
  }
}

module.exports = { assetsReducer }

接著我們來調整一下我們的 reducers 的組成,將原本的 orderReducer 置換掉:

// reducer/index.js
const { combineReducers } = require("redux");
const { assetsReducer } = require("./assetsReducer");
const { cakeReducer } = require("./cakeReducer");
const { coffeeBeanReducer } = require("./coffeeBeanReducer");
const { coffeeReducer } = require("./coffeeReducer");

const reducers = combineReducers({
  coffee: coffeeReducer,
  coffeeBean: coffeeBeanReducer,
  cake: cakeReducer,
  money: assetsReducer,
})

module.exports = {
  reducers
}

完成後,我們來測試看看結果:

initial state {
  coffee: { numOfCoffee: 20 },
  coffeeBean: { numOfCoffeeBean: 20 },
  cake: { numOfCake: 20 },
  money: { assets: 1000 }
}
更新 {
  coffee: { numOfCoffee: 18 },
  coffeeBean: { numOfCoffeeBean: 20 },
  cake: { numOfCake: 20 },
  money: { assets: 1020 }
}
更新 {
  coffee: { numOfCoffee: 38 },
  coffeeBean: { numOfCoffeeBean: 20 },
  cake: { numOfCake: 20 },
  money: { assets: 1010 }
}
更新 {
  coffee: { numOfCoffee: 38 },
  coffeeBean: { numOfCoffeeBean: 17 },
  cake: { numOfCake: 20 },
  money: { assets: 1025 }
}
更新 {
  coffee: { numOfCoffee: 38 },
  coffeeBean: { numOfCoffeeBean: 27 },
  cake: { numOfCake: 20 },
  money: { assets: 1015 }
}
更新 {
  coffee: { numOfCoffee: 38 },
  coffeeBean: { numOfCoffeeBean: 27 },
  cake: { numOfCake: 18 },
  money: { assets: 1045 }
}
更新 {
  coffee: { numOfCoffee: 38 },
  coffeeBean: { numOfCoffeeBean: 27 },
  cake: { numOfCake: 23 },
  money: { assets: 1020 }
}

出現以上訊息的話代表你成功了,至此我們已經學會如何管理多個 reducer 了,下一篇我們嘗試透過 redux-thunk 來處理一些異步處理的 function。


上一篇
Redux 深入淺出 - [ Day 8 ] 範例商品 - 蛋糕
下一篇
Redux 深入淺出 - [ Day 10 ] Redux-thunk 簡介
系列文
30天深入淺出Redux31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言