iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 20
0
自我挑戰組

React初心者30天的探索之路系列 第 20

[Day 20] 用Redux Thunk 來處理非同步action

  • 分享至 

  • xImage
  •  

Redux Thunk的目的就是為了實踐middleware,讓action能做更多的事,想必大家都曾經看過這個錯誤,當action為非同步的時候,就會報錯,Redux Thunk就是為了解決這個問題而生

來看一下Redux Thunk的原始碼

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

你沒看錯,就只有這樣。

原本的action creator 都是回傳action object,在Redux Thunk裡面會檢查若action不是object而是function,就會執行function後再將action往下傳,直到回傳object才會進去reducers

那麼接著來實作一下,情境很簡單,call api拿到資料後dispatch更新資料,讓畫面依據資料長出列表

記得要先安裝

npm install redux-thunk --save

store的部分因為比之前複雜,就從index.js抽離出來獨立,createStore可以接受三個參數(reducer、初始state、applyMiddleware),而applyMiddleware又可以傳入三個參數(thunk、promise、logger

import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk' 
import { createLogger } from 'redux-logger' 
import reducer from '../reducersBike'

const loggerMiddleware = createLogger()

export default function configureStore(preloadedState) {
    return createStore(
        reducer,
        preloadedState,
        applyMiddleware(
            thunkMiddleware,
            loggerMiddleware
        )
    )
}

順帶一提,我有另外安裝redux-logger,會在action觸發和state變化時自動印出log,個人覺得在除錯方面還蠻方便的

index.js 將store傳入

import configureStore from './redux/store'
const store = configureStore()

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

action.js

這邊為了簡單做個練習,所以只寫了一個action,處理資料接收,fetchPosts在拿到api資料後dispatch receivePosts(json.data),可以發現fetchPosts是回傳一個函式

getBikeList 可以同時拿到getState和dispatch方法

export const RECEIVE_POSTS = 'RECEIVE_POSTS'

function receivePosts(json) {
    return {
        type: RECEIVE_POSTS,
        list: json || []
    }
}

function fetchPosts() {
    return dispatch => {
        return fetch('https://api.kcg.gov.tw/api/service/Get/b4e6ae98-39b7-469b-8c68-56492cad3b71')
            .then(res => res.json())
            .then(json => dispatch(receivePosts(json.data)))
    }
}

export const getBikeList = () => {
    return (dispatch, getState) => {
        return dispatch(fetchPosts())
    }
}

reducersBike.js

經過Middleware處理後,最後將資料傳給reducer,改變state之後重新渲染UI畫面

import { RECEIVE_POSTS } from './action'

const initialState = {
    list:[]
}


const bikes = (prevState = initialState, action) => {
    switch (action.type) {
        case RECEIVE_POSTS:
            return {
                ...prevState,
                list:action.list
            }
        default:
            return prevState
    }
}

export default bikes

Bike.js

一切準備就緒之後,在componentDidMount階段dispatch getBikeList,在還沒拿到資料等待api回應之前先顯示loading,(本來是要接高雄Youbike據點的api,但api時好時壞,只好改接市場資料orz

import React, { Component } from 'react';
import { connect } from 'react-redux'
import { getBikeList } from './redux/action'

class Bike extends Component {
    componentDidMount() {
        const { list, getBikeList } = this.props
        getBikeList() 
    }

    render() {
        return (
            <div>
                <ul>
                    {
                        this.props.list?
                            this.props.list.map((item)=>{
                                return (<li>{item.市場地址}</li>)
                        })
                        :<p>loading...</p>
                    }
                </ul>
                <h1>{console.log(this.props.list)}</h1>
            </div>
        )
    }
}


const mapStateToProps = (state) => {
    return {
        list: state.list
    }
}

const mapDispatchToProps = dispatch => {
    return {
        getBikeList: () => {
            dispatch(getBikeList())
        },
    }
}

const Bikes = connect(
    mapStateToProps,
    mapDispatchToProps
)(Bike);

export default Bikes

題外話:api的key居然是中文,第一次看到

最後成功拿到列表!

除了Redux Thunk之外,再處理Middleware非同步這塊,Redux Saga也會是個不錯的選擇,日後有機會再來分析兩者的差異


上一篇
[Day 19] Redux Middleware
下一篇
[Day 21] React Hooks (上)-useState&useEffect
系列文
React初心者30天的探索之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言