看完此篇文章你能够了解到: 原文连接react
redux 的核心思想为:将须要修改的 state 都存入到 store 里,发起一个 action 用来描述发生了什么,用 reducers 描述 action 如何改变 state tree 。建立 store 的时候须要传入 reducer,真正能改变 store 中数据的是 store.dispatch API。git
中间件是 dispatch 一个 action 到触发 reducer 之间作的一个额外操做,一般使用中间件 Middleware 来进行日志记录、建立崩溃报告、调用异步接口、路由、或者改变dispatch
;github
import { createStore, applyMiddleware, combineReducers } from "redux";
import rootReducer from "./reducers/index";
import thunk from "redux-thunk";
const store = createStore(
combineReducers({ ...rootReducer }),
applyMiddleware([thunk])
);
复制代码
此处使用了异步 action 中间件 thunk,没错就是传入给 applyMiddleware 便可完成 dispatch 的加强。那么有两个问题?redux
当有多个中间件时,每个 middleware 是如何操做前一个中间件包装过的 dispatch?
如何编写本身的中间件?
applyMiddleware 便可回答第 2 个问题,applyMiddleware 函数接受一个中间件数组,并依次执行中间件,将上一个 middleware 包装过的 store.dispatch 传递给下一个中间件。数组
//一个简单的 applyMiddleware 实现(非官方的 API,后面会介绍)
function applyMiddleware(store, middlewares) {
middlewares = middlewares.slice();
middlewares.reverse(); //为什么要反序?
/** 因为是依次执行中间件,那么当前中间件执行完成确定得执行下一个中间件,作到链式调用; 之因此将列表反序的目的是为了在遍历的时候,让上一个中间件知道下一个中间件的dispatch是什么;(可能这里有点绕,下面讲述Redux API的时候会介绍) **/
let dispatch = store.dispatch;
middlewares.forEach(middleware => (dispatch = middleware(store)(dispatch)));
return Object.assign({}, store, { dispatch });
}
//提早透露:一个简单的中间件,每个中间件中须要有当前的store和下一个dispatch。
const logger = store => next => action => {
console.log("dispatching", action);
let result = next(action); //next为下一个dispatch;
console.log("next state", store.getState());
return result;
};
复制代码
理解:app
中间件的执行是顺序执行的
,为了可以链式执行中间件,须要在每个中间件中知道下一个 dispatch,这样就能够跳转到下一个中间件;异步
每一个中间件的dispatch生成实际上是反序的
,由于 A 在调用时须要知道 B 的 dispatch,B 在执行时须要知道 C 的 dispatch,那么须要先知道 C 的 dispatch。(下面 Redux API 源码会验证这点)async
在每个中间件中,都是可使用 next 函数(也就是下一个的 dispatch 函数);函数
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args); //取到当前的store
let dispatch = () => {
throw new Error(
"Dispatching while constructing your middleware is not allowed. " +
"Other middleware would not be applied to this dispatch."
);
};
const middlewareAPI = {
//每一个 middleware 接受 Store 的 dispatch 和 getState 函数做为命名参数
getState: store.getState, //返回应用当前的 state 树。
dispatch: (...args) => dispatch(...args)
};
// 依次调用每个中间件
const chain = middlewares.map(middleware => middleware(middlewareAPI));
//如今 此处的chain应该是一个函数数组[],一个相似于
/** [ function(next){ return function(action){ } }, function(next){ return function(action){ } } ] **/
//compose(...functions)从右到左来组合多个函数
//做用:compose(funcA, funcB, funcC) 形象为 compose(funcA(funcB(funcC())));
// 其效果相似于上一部分讲述的在循环中获得上一个dispatch
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
};
};
}
复制代码
理解:fetch
上面简单例子讲到,dispatch的生成实际上是反序的
能够从 compose 中看出端倪:compose(funcA, funcB, funcC) 形象为 compose(funcA(funcB(funcC())));
看 conmpose 源码其实你会发现,最后 compose(...chain)的结果应该为:
function(){
funcA(funcB(funcC()))
}
复制代码
因此在执行compose(...chain)(store.dispatch)
的时候,内部其实先调用了 funcC 来生成 C 的 dispatch。
此处可能有点超前,若是您不知道如何编写中间件请先阅读下一节,再回到这里来看
//第一个中间件
function createMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
console.log("第一个的下一个的dispatch", next);
console.log("第一个action", action);
const result = next(action);
console.log("第一个state", getState());
return result;
};
}
const firstMid = createMiddleware();
export default firstMid;
复制代码
//第二个中间件
function createMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
console.log("第二个的下一个的dispatch", next);
console.log("第二个action", action);
const result = next(action);
console.log("第二个state", getState());
return result;
};
}
const secondMid = createMiddleware();
export default secondMid;
复制代码
//中间件使用
const middlewares = [firstMid, secondMid]; //注意中间件的顺序
const store = createStore(
combineReducers({ ...rootReducer }),
composeWithDevTools(applyMiddleware(...middlewares))
);
//实际打印的结果
/** 第一个的下一个的dispatch ƒ (action) { console.log('第二个的下一个的dispatch', next); console.log('第二个action', action); var result = next(action); console.log('第二个state', getState()); return resu… 第一个action {type: "GLOBAL_DATA", globalData: {…}} 第二个的下一个的dispatch ƒ dispatch(action) { if (!isPlainObject(action)) { throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.'); } if (typeof action.type === 'unde… 第二个action {type: "GLOBAL_DATA", globalData: {…}} 第二个state {routing: {…}, global: {…}, home: {…}} 第一个state {routing: {…}, global: {…}, home: {…}} **/
复制代码
格式为:
function yourMiddleware() {
return ({ getState, dispatch }) => next => action => {};
}
复制代码
...middlewares (arguments): 遵循 Redux middleware API 的函数。每一个 middleware 接受 Store 的 dispatch 和 getState 函数做为命名参数,并返回一个函数。该函数会被传入 被称为 next 的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数;
这个函数能够直接调用 next(action),或者在其余须要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store 的 dispatch 方法做为 next 参数,并借此结束调用链。因此,middleware 的函数签名是 ({ getState, dispatch }) => next => action。
一个记录日志的中间件:
function createLoggerkMiddleware() {
return ({ dispatch, getState }) => next => action => {
console.log("will dispatch", action);
// 调用 middleware 链中下一个 middleware 的 dispatch。
let returnValue = next(action);
console.log("state after dispatch", getState());
// 通常会是 action 自己,除非
// 后面的 middleware 修改了它。
return returnValue;
};
}
const logger = createLoggerkMiddleware();
export default logger;
复制代码
理解:
const middlewares = [thunk, middleware];
const store = createStore(
combineReducers({ ...rootReducer }),
composeWithDevTools(applyMiddleware(...middlewares))
);
//action中
//这是一个同步action
const receiveInfo = response => ({
type: 'RECEIVE_HOME',
homeInfo: response
});
//使用redux-thunk异步执行action
export const getInfo = () => async (dispatch, getState) => {
try {
const response = await new Promise((resolve, reject) => {
/* 模拟异步操做成功,这样能够经过fetch调接口获取数据 */
setTimeout(() => {
resolve({ title: 'React App' });
}, 1000);
});
await dispatch(receiveInfo (response));//使用dispatch触发同步action
return response;
} catch (error) {
console.log('error: ', error);
return error;
}
};
//在react中
let {getInfo}=this.props;
getInfo().then({
})
复制代码
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
//能够接受一个返回函数的action creator。若是这个action creator 返回的是一个函数,就将dispatch的决策权交给此函数,若是不是,就按照原来的next(action)执行。
if (typeof action === "function") {
return action(dispatch, getState, extraArgument);//这就是上面例子的函数为啥接受dispatch和getState两个参数的缘由
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
复制代码