为什么要专门来处理异步呢,因为redux的设计原则要求我们的reducer必须是纯函数,所以我们在reducer去执行一些副作用的业务是不合法的。那么怎么处理异步呢?笔者认为,对于真正理解了redux的开发者来说,处理异步并不是一件难事。如果还不够深入的理解Redux以及其的中间件机制,那么以下的内容可能引起不适,会看的云里雾里,建议翻看笔者第一篇文章从0到1重新撸一个Redux
redux-thunk
先来看看源码吧
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
// 判断dispatch的参数是否是函数,是函数就把dispatch getstate 参数交出
// 让函数自己去玩
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
原理很简单,dispatch不仅可以dispatch原有的action,也可以dispatch函数,中间件thunk发现action是函数则把所有的核心方法和参数交出。看看怎么实际应用
import fetch from 'fetch'
// 先设计一个异步请求
export function fetchNews(id) {
// Thunk middleware 知道如何处理函数。
// 这里把 dispatch 方法通过参数的形式传给函数,
// 以此来让它自己也能 dispatch action。
return function (dispatch) {
// 首次 dispatch:更新应用的 state 来通知
// thunk middleware 调用的函数可以有返回值,
// 它会被当作 dispatch 方法的返回值传递。
// 这个案例中,我们返回一个等待处理的 promise。
// 这并不是 redux middleware 所必须的,但这对于我们而言很方便。
return fetch(`/api/getNews`, {id})
.then(
response => response.json(),
error => console.log('An error occurred.', error)
)
.then(json =>
dispatch({
name: 'add_news',
data: json
})
)
}
}
把异步的业务放进redux中去
const store = createStore(
rootReducer,
applyMiddleware(
thunkMiddleware, // 允许我们 dispatch() 函数
)
)
store.dispatch(fetchNews(1)).then(() => console.log(store.getState()))
如此一来,我们把异步的业务封装一下,把他当做一个异步action交给reudx的dispatch便可轻松解决redux本身不支持异步的问题。那么试问是否还有更好的异步解决方法呢?回到redux和异步业务本身,最为关键和直接的一点便是异步中业务可以dispatch,如果满足这个需求无论什么方法都可以解决redux异步的问题。
Promise.then与store
直接看吧
// redux module
// 直接导出store
export const store = createStore(
rootReducer
)
// business module
import * as React from 'react';
import './index.less';
import {store} from './redux'
class Header extends React.Component {
constructor(props: LogoProps) {
super(props);
}
componentDidMount() {
fetch(`/api/getNews`, {id: 1})
.then(
response => response.json(),
error => console.log('An error occurred.', error)
)
.then(json =>
store.dispatch({
name: 'add_news',
data: json
})
)
}
render() {
return (<div>
hello world
</div>);
}
}
export default Header;
如此一来,连中间件都不需要就可以轻松解决异步,但是这样不利于代码管理,我们可以按模块的把每一个异步业务给聚合起来。
async/awit和BaseAction
先创建一个封装了store的dispatch的基类
import {store} from './redux';
export default class ActionModeBase {
dispatch(params) {
store.dispatch(params);
}
}
再创建一个处理请求新闻业务的类
import BaseAction from './ActionModeBase';
import fetch from 'fetch'
export default class HandleNewsMode {
getNews = async (id) => {
const news = await fetch('/api/getNews', id);
this.dispatch({
name: 'add_news',
data: news
})
}
}
业务方引入异步业务的类
import * as React from 'react';
import './index.less';
import HandleNewsMode from './HandleNewsMode'
class Header extends React.Component {
constructor(props: LogoProps) {
super(props);
// 初始化
this.businessMode = new HandleNewsMode();
}
componentDidMount() {
this.businessMode.getNews(1);
}
render() {
return (<div>
hello world
</div>);
}
总结
处理redux的异步并不是一件难事,redux-thunk也不是万金油,对于更为复杂的异步场景笔者认为通过内聚异步业务+async更能胜任。