Redux 入門 -- 處理 async action

阿希發表於2018-04-18

本文目標:希望通過買進口水果生鮮的例子,和大家探討一下如何處理非同步的 async action

例子:買進口水果生鮮

在上一篇文章 Redux入門 -- 拆分 reducer 中,阿大通過 reduxbindReducers 方法將水果店的業務分治成功,店鋪也越做越大。以至於有顧客開始想要買一些進口的水果生鮮。

阿大考慮了一下,決定繼續擴充這個店鋪,從事進口商品的銷售。首先是顧客的需求行為需要購買進口水果生鮮:

// 買水果 - 進口蘋果
+ function buyImportedApple(num) {
+   return {
+     type: 'BUY_IMPORTED_APPLE',
+     payload: {
+       num
+     }
+   }
+ }

// 買生鮮 - 進口雞蛋
+ function buyImportedEgg(num) {
+   return  {
+     type: 'BUY_IMPORTED_EGG',
+     payload: {
+       num
+     }
+   }
+ }
複製程式碼

然後水果部和生鮮部的賬本也要更新啦:

// 水果賬本
 const fruitState = {
   orange: 0,
   apple: 0,
   banana: 0,
+  importedApple: 0
 };

 // 生鮮賬本
 const freshState = {
   egg: 0,
   fish: 0,
   vegetable: 0,
+  importedEgg: 0
 };
複製程式碼

同樣的,相應部門的收銀員們也要學會怎麼處理進口水果生鮮的記賬,他們的記賬方式要改成下面這樣:

// 水果部收銀員
function fruitReducer(state = fruitState, action) {
  
  // 如果有人買了相應的水果,更新賬本
  switch (action.type) {
    case 'BUY_APPLE':
      return Object.assign({}, state, {
        apple: state.apple + action.payload.count
      });
    case 'BUY_IMPORTED_APPLE':
      return Object.assign({}, state, {
        importedApple: state.importedApple + action.payload.count
      });

    // 買其他的東西,不更新賬本,原樣返回
    default: return state;
  } 
}

// 生鮮部收銀員
function freshReducer(state = freshState, action) {
  switch (action.type) {
    case 'BUY_EGG':
      return Object.assign({}, state, {
        egg: state.egg + action.payload.count
      });
    case 'BUY_IMPORTED_EGG':
      return Object.assign({}, state, {
        importedEgg: state.importedEgg + action.payload.count
      });
    default: return state;
  } 
}
複製程式碼

可是這時候阿大發現,進口水果生鮮不能大量存在自己倉庫賣,因為它們又貴又容易壞,只有當顧客需要買的時候,才能去採購這些水果生鮮,於是阿大又僱了一個採購員專門負責處理要買進口水果和生鮮的顧客,等到貨了再通知銷售員取貨給顧客:

// 採購商品生成器,不同的商品需要不同的時間採購
function fetchGoodsGenerator(time, action) {

  // 用延時模擬採購時間
  const timer = setTimeout(() => {
    clearTimeout(timer);

    // 採購完成,通知銷售員
    store.dispatch(action);
  }, time);
}

// 採購進口蘋果需要 2 天(2s)
function fetchImportedApple(action) {
  fetchGoodsGenerator(2000, action);
}

// 採購進口雞蛋需要 3 天(3s)
function fetchImportedEgg(action) {
  fetchGoodsGenerator(3000, action);
}

// 採購員
const API = {
  fetchImportedApple, // 採購進口蘋果
  fetchImportedEgg // 採購進口雞蛋
}
複製程式碼

好了,佈置完了之後,顧客開始來買水果生鮮了:

// 銷售員開始銷售,採購員開始採購
store.dispatch(buyApple(3));
API.fetchImportedApple(buyImportedApple(10));
store.dispatch(buyEgg(1));
API.fetchImportedEgg(buyImportedEgg(10));
store.dispatch(buyApple(4));
API.fetchImportedApple(buyImportedApple(10));
store.dispatch(buyEgg(8));
API.fetchImportedEgg(buyImportedEgg(10));
// {"fruit":{"apple":3,"importedApple":0},"fresh":{"egg":0,"importedEgg":0}}
// {"fruit":{"apple":3,"importedApple":0},"fresh":{"egg":1,"importedEgg":0}}
// {"fruit":{"apple":7,"importedApple":0},"fresh":{"egg":1,"importedEgg":0}}
// {"fruit":{"apple":7,"importedApple":0},"fresh":{"egg":9,"importedEgg":0}}
// {"fruit":{"apple":7,"importedApple":10},"fresh":{"egg":9,"importedEgg":0}}
// {"fruit":{"apple":7,"importedApple":20},"fresh":{"egg":9,"importedEgg":0}}
// {"fruit":{"apple":7,"importedApple":20},"fresh":{"egg":9,"importedEgg":10}}
// {"fruit":{"apple":7,"importedApple":20},"fresh":{"egg":9,"importedEgg":20}}
複製程式碼

現在水果生鮮店的進口業務也穩妥了,阿大心裡美滋滋~

講解

在實際的開發當中我們經常會呼叫一些 API 介面獲取資料更新 state。剛開始使用 redux 的一個誤區就是在 reducer 裡接收到非同步的 action 之後,就在 reducer 裡做非同步操作,呼叫 API。但是這樣是錯誤的。reducer 只能是純函式,不能有任何副作用。這樣才能保證對於相同的輸入,一定會有相同的輸出。

圖解

Redux 入門 -- 處理 async action

程式碼地址:Redux 入門 -- 處理 async action,直接控制檯執行 node ./demo3/index.js 檢視效果

上一篇:Redux入門 -- 拆分 reducer

下一篇:Redux 進階 -- 優雅的處理 async action

相關文章