• [React]Redux中间件与异步操作


    来源:Redux入门教程-阮一峰
    文档:Redux中文文档

    中间件

    同步操作:在Action发出以后,Reducer立即返回新的State;
    异步操作:在Action发出以后,过一段时间才执行Reducer;
    中间件是使Reducer在异步操作结束后自动执行的工具

    中间件的概念

    中间件的功能是在异步操作结束之后能够自动执行Reducer更新state,这个功能应该被添加在发送Action的功能中,即store.dispatch()。
    例如,在下面的例子中,将会对store.dispatch()进行改造,使其能够将Action和State打印。

    let next=store.dispatch;
    store.dispatch=function dispatchAndLog(action){
        console.log('dispatching',action);
        next(action);
        console.log('next state',store.getState());
    }
    

    以上的dispatch()将会在发送Action的前后进行打印。

    中间件的用法

    一般常见的中间件都已经被编写好了,只要通过引用即可获得,引用的方法为:

    import {applyMiddleware,createStore} from 'redux';
    import createLogger from 'redux-logger';
    const logger=createLogger();
    
    const store=createStore(
        reducer,
        applyMiddleware(logger)
    )
    

    在上面的例子中,添加了一个日志中间件,将其放入applyMiddleware()方法中作为参数传入createStore()。
    注意:

    • createStore()方法可以接收整个应用的初始状态作为参数,在接受初始状态时,applyMiddleware是第三个参数;
    const store = createStore(
      reducer,
      initial_state,
      applyMiddleware(logger)
    );
    
    • 中间件的次序需要注意
      中间件的次序在有些时候有些要求,使用前要检查一下文档,比如logger一定要放在最后。

    异步操作的基本思路

    同步操作只要发出一种Action,但是异步操作要发出三种Action:

    • 操作发起时的Action
    • 操作成功时的Action
    • 操作失败时的Action

    在向服务器获得数据时,三种Action可以有两种不同的写法:

    //写法1:名称相同,参数不同
    {type:'FETCH_POSTS'}
    {type:'FETCH_POSTS',status:'error',error:'0ops'}
    {type:'FETCH_POSTS',status:"success",response:{...}}
    
    //写法2:名称不同
    { type: 'FETCH_POSTS_REQUEST' }
    { type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
    { type: 'FETCH_POSTS_SUCCESS', response: { ... } }
    

    Action种类不同,且异步操作的State也要进行改造,反映不同的操作状态。

    let state = {
      // ... 
      isFetching: true,
      didInvalidate: true,
      lastUpdated: 'xxxxxxx'
    };
    

    在上面的例子中,定义了State的属性isFetching是否在获得数据,didInvalidate表示数据是否过时,lastUpdated表示上一次更新时间。
    在异步操作过程中,需要:

    • 在操作开始时,送出一个Action,触发State更新为"正在操作"状态,View重新渲染;
    • 操作结束后,再送出一个Action,触发State更新为"操作结束"状态,View再一次渲染。

    redux-thunk中间件

    异步操作需要送出两个Action:用户在操作开始时发送的第一个Action,在结束时送出第二个Action;
    这需要在Action Creator中进行操作:

    class AsyncApp extends Component {
      componentDidMount() {
        const { dispatch, selectedPost } = this.props
        dispatch(fetchPosts(selectedPost))
      }
    
    // ...
    

    在加载结束后生命周期函数内送出Action,向服务器请求数据(fetchPosts(selectedSubreddit)),这里的fetchPosts是Action Creator。

    fetchPosts

    fetchPosts的代码为:

    const fetchPosts = postTitle => (dispatch, getState) => {
      //第一个Action,表示异步操作开始
      dispatch(requestPosts(postTitle));
      return fetch(`/some/API/${postTitle}.json`)//获取数据
        .then(response => response.json())//将数据转化为JSON
        .then(json => dispatch(receivePosts(postTitle, json)));//发送第二个Action,表示异步操作结束
      };
    };
    

    fetchPosts是一个Action Creator,返回一个函数,函数执行之后将会发出一个Action,进行异步操作得到结果后将会在对结果进行处理之后再发一个Action。
    在上面的例子中,

    • fetchPosts返回一个函数,普通的Action Creator默认返回一个对象;
    • 返回函数的参数是dispatchgetState这两个Redux方法,普通的Action Creator的参数是Action的内容。

    存在问题:在通常情况下,store.dispatch()的参数为Action对象,不能是函数
    为了解决整个问题,使用中间件redux-thunk;

    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import reducer from './reducers';
    
    // Note: this API requires redux@>=3.1.0
    const store = createStore(
      reducer,
      applyMiddleware(thunk)
    );
    

    使用中间件改造后的store.dispatch()将可以接受函数作为参数。
    这是异步操作的第一个解决方案。

    redux-promise

    另一个异步操作的解决方案时让Action Creator返回一个Promise对象,这需要redux-promise中间件的支持。

    import { createStore, applyMiddleware } from 'redux';
    import promiseMiddleware from 'redux-promise';
    import reducer from './reducers';
    
    const store = createStore(
      reducer,
      applyMiddleware(promiseMiddleware)
    ); 
    

    这个中间件使得store.dispatch()可以接收Promise对象作为参数,这时有两种写法,一种是将Action Creator的返回值设置为一个Promise对象;
    另一种是将Action的payload属性设置为Promise对象,这需要从redux-actions引入createAction方法。
    两种写法如下:

    //第一种
    const fetchPosts = 
      (dispatch, postTitle) => new Promise(function (resolve, reject) {
         dispatch(requestPosts(postTitle));
         return fetch(`/some/API/${postTitle}.json`)
           .then(response => {
             type: 'FETCH_POSTS',
             payload: response.json()
           });
    });
    
    //第二种
    import { createAction } from 'redux-actions';
    
    class AsyncApp extends Component {
      componentDidMount() {
        const { dispatch, selectedPost } = this.props
        // 发出同步 Action
        dispatch(requestPosts(selectedPost));
        // 发出异步 Action
        dispatch(createAction(
          'FETCH_POSTS', 
          fetch(`/some/API/${postTitle}.json`)
            .then(response => response.json())
        ));
      }
    

    createAction的第二个参数必须是一个Promise对象。
    对于redux-promise的源码进行分析:

    export default function promiseMiddleware({ dispatch }) {
      return next => action => {
        
        if (!isFSA(action)) {
          return isPromise(action)//Action是一个Promise
            ? action.then(dispatch)//resolve的值将会被送出,reject无动作
            : next(action);
        }
    
        return isPromise(action.payload)//Action的payload属性是一个Promise
          ? action.payload.then(
              result => dispatch({ ...action, payload: result }),//resolve发出Action
              error => {//reject发出Action
                dispatch({ ...action, payload: error, error: true });
                return Promise.reject(error);
              }
            )
          : next(action);
      };
    }
    
  • 相关阅读:
    一手遮天 Android kotlin 协程: 通过 ticker 信道实现类似计时器的效果,协程的异常处理,解决协程的并发问题
    一手遮天 Android jetpack: LiveData 指定的对象的某个属性发生了变化时通知给观察者
    一手遮天 Android jetpack: DataBinding(MVVM)
    一手遮天 Android kotlin 协程: Channel(信道,用于在不同协程之间传输数据)
    一手遮天 Android jetpack: LiveData 基础,以及 LiveData 和 ViewModel 结合使用
    一手遮天 Android kotlin 协程: 协程基础(CoroutineScope, 为 CoroutineScope 扩展方法, runBlocking, launch, async, await, suspend, withContext, 设置/获取 CoroutineScope 的名称)
    一手遮天 Android UI: 监听配置变化(比如横竖屏切换等)
    一手遮天 Android jetpack: ViewModel 基础以及 viewModelScope
    一手遮天 Android Resource: 布局 xml 基础
    一手遮天 Android kotlin 协程: Flow(异步流,各种操作符的使用 buffer, conflate, collectLatest, drop, take, filter, map, transform, onEach, first, last, single, reduce, zip, combine, flatMapConcat, flatMapMerge 等)
  • 原文地址:https://www.cnblogs.com/liuju/p/12639530.html
Copyright © 2020-2023  润新知