上节课我们介绍了redux的完整流程中的Action Creators这个环节,我们到这里其实已经可以通过redux来对组件的状态来进行集中化管理了,但是我们里面还有一些高级操作,这节课我们就来先介绍其中的一个,叫异步action

回顾

那么在介绍异步action之前呢,我们来对上节课的内容来做一个简单的回顾:

  • Action Creators本质上其实也是函数
  • 我们通过常量模块来定义动作类型,防止单词写错

以上便是上节课的主要要点,接下来我们来介绍异步action

什么是异步action

我们之前在课上说,我们的这个action并不是什么高大上的东西,其实哪个玩意儿就是仅仅只是一个原生js中的一个object类型的普通对象,我们甚至都不需要通过Action Creator来创建,直接手写都行,但是现在我来告诉大家,这个action,不仅仅可以是一个普通对象,他还可以是一个函数。我们管这种一般对象类型的action叫做同步action,而那种函数类型的action,我们称之为异步action

需求描述

我们说了上面这两个概念,大家是不是觉得很懵,什么意思?不理解啊。我们先别着急,我们通过案例来去理解。

我们先来思考一个问题,我们页面上的异步加的功能是怎么实现的?是不是通过setTimeout方法配合一个执行器函数来完成的?我们代码中是要延迟 1 秒之后再加,那么我来问一个问题,这个延迟的 1 秒钟的时间是在哪里延迟的?是不是在我们的组件中?那么现在我们要求这个等待的事件不在组件中发生,我们要把等待的事件交给action

需求分析

我们要求等待事件交给action的话,那么我们组件中是不是就不能再调用setTimeout函数了?但是不调用setTimeout的话我们不就不等待了嘛,那么我们是不是就要在Action Creator上面做手脚了?那么我们就不能用之前的函数了啊,我们就需要重新定义一个新的函数。

实现

我们现在思路已经有了,那么怎么实现呢?

// sum_action_creator.js
import { INCREMENT, DECREMENT } from "./constant"
import store from "./store";

export const createIncrementAction = data => ({ type: INCREMENT, data });
export const createDecrementAction = data => ({ type: DECREMENT, data });
export const createAsyncIncrementAction = (data, time) => {
  return () => setTimeout(() => {
    store.dispatch(createIncrementAction(data))
  }, time);
}
// Sum 组件
import React, { Component } from 'react'
import store from '../../redux/store';
import { 
  createIncrementAction, 
  createDecrementAction, 
  createAsyncIncrementAction 
} from '../../redux/sum_action_creator';

export default class Sum extends Component {
  componentDidMount() {
    store.subscribe(() => this.setState({}));
  }
  render() {...}
  ...
  async = () => {
    const { value } = this.selectNum;
    store.dispatch(createAsyncIncrementAction(value * 1, 1000));
  }
}

我们来看代码,我们在组件中调用了createAsyncIncrementAction函数,而且传了数据和要延迟的时间。但是我们有这个函数吗?是不是没有?那么我们就要在sum_action_creator.js中来定义这个函数,那么我们来看这个函数的代码。首先我们接收数据和要延迟的时间这两个参数。然后返回一个函数。为什么我们要返回一个函数?因为我们要写一个异步action,那么为什么异步action就要返回一个函数呢?因为函数中可以开启异步任务,像对象啊,数组啊什么的都没法开启异步任务,所以说异步action得返回一个函数。

那么这个函数里面我们做什么呢?首先是不是要开启一个定时器?等延迟的时间到了之后我们做什么?是不是把动作分发出去,那么就交给store吧,交给store直接分发一个动作对象给reducer就行了啊,那么动作对象从哪来?创建动作对象我们是不是早就写好了?直接调用createIncrementAction然后把数据传进去就可以了。那么我们来看一下效果吧。

image-20220119100852151

报错了告诉我们action必须是一个普通对象,我们需要通过一个中间件来实现。因为store这个东西是不支持分发函数的,所以说我们需要通过一个中间件来使得store接收到函数之后不分发给reducer,而是直接执行这个函数。我们看我们的action,这个action返回的是一个函数,这个函数也没有调用,那么这么往下进行啊?是不是要store来执行这个函数?而store执行函数之后马上开启一个定时器,等时间到了之后重新创建一个加的action交给store再由store分发给reducer,那么要使用什么中间件呢?

我们来安装一下npm i redux-thunk,我们就是靠这个中间件来实现让store来调用我们的异步action

import { createStore, applyMiddleware } from 'redux';
import sumReducer from './sum_reducer';
import thunk from 'redux-thunk';

export default createStore(sumReducer, applyMiddleware(thunk));

我们来看一下代码,我们的这个中间件是不是作用于store上的?那么好,我们是不是要把这个中间件在store中导入?然后我们怎么能使得这个store能够正常使用这个中间件呢?我们就要通redux的一个函数applyMiddleware来在创建store的时候就来实现中间件的加载。那么我们这次再来看一下效果吧:

iShot2022-01-19 10.26.18

这一次我们的效果就正常实现了。

但是呢我们的代码还有一个不完善的地方。我们是不是在定时器的执行器函数中通过store来调用了dispatch?这样的话我们就要引入store,但是实际上呢我们的这个action返回的函数是不是store调用的?store在调用这个函数的时候就已经把dispatch函数传过去了,只是我们没有接,那么我们来做一下修改:

import { INCREMENT, DECREMENT } from "./constant"

export const createIncrementAction = data => ({ type: INCREMENT, data });
export const createDecrementAction = data => ({ type: DECREMENT, data });
export const createAsyncIncrementAction = (data, time) => dispatch => setTimeout(
  () => dispatch(createIncrementAction(data)), time
);

这样的话我们连store都不需要再引入了。

总结

  • 异步操作不想在组件自身操作时交给异步action
  • 异步action中通常会调用同步action
  • 异步action需要中间件redux-thunk的支持

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/demo_react_redux_async_action/