上节课我们介绍了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
然后把数据传进去就可以了。那么我们来看一下效果吧。
报错了告诉我们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
的时候就来实现中间件的加载。那么我们这次再来看一下效果吧:
这一次我们的效果就正常实现了。
但是呢我们的代码还有一个不完善的地方。我们是不是在定时器的执行器函数中通过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/