你不知道的redux系列 | redux如何实现异步

900 阅读2分钟

redux基础知识 | redux(二)

在上一篇文章中,我们经实现了一个基本的redux,那如果我们想在redux中实现异步的操作怎么办?

比如下面的操作

pages/ReduxPage.js

import React, { Component } from 'react'
import store from '../store/'

export default class ReduxPage extends Component {
    componentDidMount() {
        this.unsubscribe = store.subscribe(() => {
            this.forceUpdate()
        })
    }
    componentWillUnmount() {
        if(this.unsubscribe) {
            this.unsubscribe()
        }
    }
    asyAdd = () => {
        store.dispatch((dispatch,getState) => {
            setTimeout(() => {
                dispatch({type: 'ADD', payload: 1})
            }, 1000)
        })
    }
    promiseAdd = () => {
        store.dispatch(Promise.resolve({type: "MINUS", payload: 1}));
    }
    render() {
        return(
            <div>
                <h3>Reduxpage</h3>
                <p>{count}</p>
                <button onClick={this.asyAdd}>异步+</button>
                <button onClick={this.promiseAdd}>promise-</button>
            </div>
        )
    }
}

上面的操作我们有两个异步的操作,分别是是setTimeout和Promise。由于我们的action触发了以后,就会立马去reducer里面执行state的更新,所以我们想要实现异步的操作,就必须在dispatch的时候,去做一些操作。

在store里面,有个方法叫做applyMiddleware(),它可以接收一些参数,二接受的这些参数,就是我们处理异步的中间件,我们看一下怎么使用的。

import { createStore, applyMiddleware } from 'redux';
import logger from 'react-logger';
import thunk from 'react-thunk';
import rdPromise from 'react-promise'

const countReducer = (state = 0, action) => {
    const {type, payload} = action
    switch(type) {
        case 'ADD':
            return state + payload;
        case 'MINUS':
            return state - payload;
        default:
            return state
    }
}

const store = createStore(
    countReducer,
    applyMiddleware(thunk, logger, rdPromise)
)
export default store;

applyMiddleware()接收了三个参数:

  • thunk,处理setTimeout这种异步
  • logger,打印state日志
  • rdPromise,处理promise异步

这样子,我们上面的异步操作按钮就可以正常的工作了,下面让我们看看源码如何实现的。

Jreact/createStore.js

export default function createStore(reducer, enhancer) {
    if(enhancer) {
        return enhancer(createStore)(reducer)
    }
}

在createStore里面,我们需要判断一下createStore的第二个参数,也就是applyMiddleware,有的话,就把createStore和reducer传给它,我们需要在内部去使用这两个参数。

Jreact/applyMiddleware.js

export default function applyMiddleware(...middlewares) {
    return createStore => reducer => {
        const store = createStore(reducer)
        let dispatch = store.dispatch

        const midApi = {
            getState: store.getState,
            dispatch: (action, ...args) => dispatch(action, ...args)
        }

        const middlewareChain = middlewares.map(md => md(midApi))
        dispatch = compose(...middlewareChain)(store.dispatch);

        return {
            ...store,
            dispatch
        }
    }
}

// 聚合函数
function compose(...funcs) {
    if (funcs.length === 0) {
      return arg => arg;
    }
    if (funcs.length === 1) {
      return funcs[0];
    }
    return funcs.reduce((a, b) => (...args) => {
      return a(b(...args));
    });
  }

applyMiddleware(...middlewares)就是接收的thunk, logger, rdPromise这几个参数,然后在里面对dispatch做了加强。我们对middlewares做了便利,也就是thunk, logger, rdPromise,他们会接收getState和dispatch,然后在每个中间件的内部独立处理自己的逻辑,处理完成以后会形成一个middlewareChain,使用一个聚合函数compose,把middlewareChain里面的函数聚合一个,加强了dispatch,最后返回出去,这样就可以在dispatch的时候,做一些操作了。

下面是三个中间件的实现

Jreact/thunk.js

export default function thunk({dispatch, getState}) {
    return next => action => {
        if(typeof action === 'function') {
            return action(dispatch, getState)
        }
        return next(action)
    }
}

Jreact/logger.js

export default function logger({getState}) {
    return next => action => {
        console.log('prevState', getState());
        const newState = next(action)
        console.log('nextState', getState());
        return newState
    }
}

Jreact/rdPromise.js

import { isFSA } from 'flux-standard-action';
import isPromise from 'is-promise';

export default function rdPromise({dispatch}) {
    return next => action => {
        if(!isFSA(action)) {
            return isPromise(action) ? action.then(dispatch) : next(action)
        }
        return next(action)
    }
}

这是三个函数,通过聚合函数依次执行他们,就可以了。

至此,redux的源码就讲的差不多了

参考链接