在上一篇文章中,我们经实现了一个基本的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的源码就讲的差不多了