2019年2月27日 星期三

redux-thunk react 執行非同步動作

在 redux 中執行非同步的動作



使用

redux-thunk

applyMiddleware

一般流程 在 dispatch 後會將 action 的內容傳給 reducer

在非同步的動作下 會將一般流程的部分 放在 非同步的 callback 下 非同步執行完後 接著執行
以下是我個人的寫法

actions
將兩個 action 包裝成 async 的 function

const setNum = setNum =>({
  type:"SETNUM" , 
  num:setNum
})


const asyncAdd = function(){

  return async function(dispatch , state){
    var n = await asyncadd();
    return n;
  }
}

const asyncMinus = function(){
  return async function(dispatch , state){
    var n = await asyncminus();
    return n;
  }
}

async  function asyncadd()
{
  return new Promise(function(resolve , reject){
      setTimeout(function(){
        resolve(1);
      } , 500)
    })
}

async  function asyncminus()
{
  return new Promise(function(resolve , reject){
      setTimeout(function(){
        resolve(-1);
      } , 500)
    })
}
export {setNum , asyncAdd , asyncMinus}

reducer

reducer 無法執行 非同步動作

基本上就是負責 非同步結束後 state 的變化

  const reducer = (state={'value':0}, action) => {

   switch (action.type) {
     case 'SETNUM':
       return {'value':state['value']+action.num};
     default:
       return {'value':state['value']};
   }
 }

 export default reducer

container

再傳給 component 的 function 中 將 非同步動作進行 return

import React from 'react';
import Counter from '../component/Counter';
import {setNum , asyncAdd , asyncMinus} from '../actions/action'
import { connect } from 'react-redux';


const mapStateToProps = state =>({value:state});

const mapDispatchToProps = (dispatch , props) => ({
    onAsyncIncrement:function(){  return dispatch(asyncAdd())} , 
    onAsyncDecrement:function(){return dispatch(asyncMinus())},
    setNum:function(num){dispatch(setNum(num))}
    }
);

export default connect(mapStateToProps ,mapDispatchToProps )(Counter);


component

將 container 傳的 function 在這邊進行包裝 將從 action 裡面回傳的 promise 寫入 resolve

import React from 'react';

export default class Counter extends React.Component
{
  constructor(props) {
    super(props);
  }
  asyncIncrement()
  {
    this.props.onAsyncIncrement().then(function(num){
        this.props.setNum(num);

    }.bind(this))
  }
  asyncDecrement()
  {
      this.props.onAsyncDecrement().then(function(num){
        this.props.setNum(num);

    }.bind(this))
  }
  render() {

    return(
            <p>
                Clicked: {this.props.value.value} times
                <button onClick={this.asyncIncrement.bind(this)}>
                    async +
                </button>
                <button onClick={this.asyncDecrement.bind(this)}>
                    async -
                </button>
            </p>
            );
  }
}
 
最後 整合

babel-polyfill 在出現 regeneratorRuntime is not defined 需引入

在 createStore 的地方

需使用 applyMiddleware 來使非同步動作產生效果

反則不會正常動作

import "babel-polyfill";  //regeneratorRuntime is not defined
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore , applyMiddleware} from 'redux';
import {Provider} from 'react-redux';
import Counter from './container/container'
import reducer from './reducer/index'
import thunk from 'redux-thunk';

const store = createStore(
  reducer,
  applyMiddleware(thunk)
);

ReactDOM.render(
   <Provider store={store}>
            <Counter />
  </Provider>,
  document.getElementById('app')
);
code

範例

沒有留言:

張貼留言