iT邦幫忙

2022 iThome 鐵人賽

DAY 11
0
自我挑戰組

30天深入淺出Redux系列 第 11

Redux 深入淺出 - [ Day 11 ] Redux-thunk 非同步 Action

  • 分享至 

  • xImage
  •  

上篇我們已經完成基本的redux-thunk的安裝,那麼今天我們就來設定一組簡單的資料呈現功能吧!

這邊為了示範常用的作法我會採用 axios 來做資料的串接,當然這部分大家選自己習慣的工具去處理即可,那麼如果和我一樣要使用 axios 的朋友記得安裝套件於你的專案當中。

npm install axios
or
yarn add axios

這邊為了方便示範我們就用 pokemon api 來做個簡單的練習吧!

pikachu

首先我們可以先到他的網站來看看都有哪些參數可以應用。— 連結在此

試想我們簡單的處理一個列表,功能的話簡單的上下頁,每做一次 call 一次 api,我們會需要用到的參數有 limit 和 offset,分別代表了一次哪多少資料和從第幾個資料開始,那麼他的 response 回傳上一頁和下一頁的 url 與資料本身。

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

那麼知道他會回傳整串的 url 之後我這邊就不再拆解原本的 url 會嘗試以簡單的形式示範,那麼讓我們回到專案裡,先去定義拿 api 的時候會發生的情況,通常會有以下三種:

  • 正在拿取資料…
  • 成功拿取並回傳資料
  • 錯誤回吐錯誤訊息

那我們就於 action/types.js 的檔案下先行定義這三種情況:

// action types.js
// 省略...
const POKEMON_PENDING = 'POKEMON_PENDING';
const POKEMON_SUCCESSED = 'POKEMON_SUCCESSED';
const POKEMON_FAILED = 'POKEMON_FAILED';

module.exports = {
  // 省略...
  POKEMON_PENDING,
  POKEMON_SUCCESSED,
  POKEMON_FAILED
};

那麼我們接著設定以上三種情況的 action creator,於資料夾內新增 pokemon.js 的檔案,如下:

// action/pokemon.js
const axios = require('axios');
const { POKEMON_PENDING, POKEMON_SUCCESSED, POKEMON_FAILED } = require('./types');

// 這裡為單純修改 boolean 值
const fetchPokeRequest = () => {
  return {
    type: POKEMON_PENDING
  }
}

// 這裡是成功回傳,要將資料存入 initialState
const fetchPokeSuccess = pokemons => {
  return {
    type: POKEMON_SUCCESSED,
    payload: pokemons
  }
}

// 這裡是錯誤訊息的處理
const fetchPokeFailure = error => {
  return {
    type: POKEMON_FAILED,
    payload: error
  }
}

// 這裡作法為將 url 提列出來,因為要方便上下頁使用
const fetchPokemons = (url) => {
  // 對你沒看錯,在redux-thunk安裝之後可以讓你回傳 function 並保留 dispatch 的功能
  // 可以直接在 return function 內做使用。
  return function (dispatch) {
    dispatch(fetchPokeRequest())
    axios
      .get(url)
      .then(response => {
        // 裏面包含上下頁的 url 和 pokemons data
        const pokesData = response.data
        dispatch(fetchPokeSuccess(pokesData))
      })
      .catch(error => {
        // 這裡處理錯誤訊息回傳
        dispatch(fetchPokeFailure(error.message))
      })
  }
}

module.exports = {
  fetchPokeRequest,
  fetchPokeSuccess,
  fetchPokeFailure,
  fetchPokemons
}

接著我們來處理 reducer 的部分,如以下:

// reducer/pokemonReducer
const { POKEMON_PENDING, POKEMON_SUCCESSED, POKEMON_FAILED } = require("../action/types");

const initialState = {
  loading: false,
  data: [],
  error: ''
}

const pokemonReducer = (state = initialState, action) => {
  switch (action.type) {
    case POKEMON_PENDING:
      // loading
      return {
        ...state,
        loading: true
      } 
    case POKEMON_SUCCESSED: 
			// success
      return {
        loading: false,
        data: action.payload,
        error: ''
      }
    case POKEMON_FAILED:
			// fail 
      return {
        loading: false,
        data: [],
        error: action.payload
      }
    default: 
      return state;
  }
}

module.exports = { pokemonReducer }

一樣要記得在 reducers 的地方加入剛剛寫好的 reducer:

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

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

module.exports = {
  reducers
}

然後我們回到 index.js 的地方來測試剛剛的 pokemon,我們把剛剛的一些指令先碼掉並加入以下:

// ./index.js
// 省略...
store.dispatch(fetchPokemons(`https://pokeapi.co/api/v2/pokemon?offset=0&limit=20`))

要記得將原本的 unsubscribe() 給碼掉,不然他會清掉動作。

測試成功的話會於終端機得到以下訊息:

initial state {
  coffee: { numOfCoffee: 20 },
  coffeeBean: { numOfCoffeeBean: 20 },
  cake: { numOfCake: 20 },
  money: { assets: 1000 },
  pokemon: { loading: false, data: [], error: '' }
}
更新 {
  coffee: { numOfCoffee: 20 },
  coffeeBean: { numOfCoffeeBean: 20 },
  cake: { numOfCake: 20 },
  money: { assets: 1000 },
  pokemon: { loading: true, data: [], error: '' }
}
更新 {
  coffee: { numOfCoffee: 20 },
  coffeeBean: { numOfCoffeeBean: 20 },
  cake: { numOfCake: 20 },
  money: { assets: 1000 },
  pokemon: {
    loading: false,
    data: {
      count: 1154,
      next: 'https://pokeapi.co/api/v2/pokemon?offset=20&limit=20',
      previous: null,
      results: [Array]
    },
    error: ''
  }
}

至此,我們已經介紹完 redux 的基礎應用了,下一篇我會和大家介紹 redux-toolkit。

這邊附上這個章節完成的範例 source code


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

尚未有邦友留言

立即登入留言