简介
Redux 是 JavaScript 状态容器,提供可预测化的状态管理,不依赖于任何第三方插件,例如vue也可以使用redux进行状态管理
redux常用的api有5种:
- createStore:创建
store容器 - applyMiddleware:封装redux中间件函数
- bindActionCreators:一般在react-redux中使用,对多个
action函数进行封装,返回带有dispatch的自动调用的对象 例如({ action1, action2 }) => return { action1: dispatch(action1), action2: dispatch(action2) } - combineReducers:将多个reducer的对象转换成一个大的reducer 例如({ reducer1, reducer2 }) => return (preState, action) => return { reducer1: newReducer1, reducer2: newReducer2 }
- compose:将函数数组[fn1, fn2, fn3]转换成嵌套函数(...args) => fn3(fn2(fn1(...args)))
安装redux:
npm install --save redux
基本使用
redux通过createStore创建状态容器store,返回一个对象{ getState:返回状态的函数,dispatch:(action) => 触发reducer更新state,subscribe:(listener:Function) => dispatch触发时执行listener回调 },createStore接受两个参数
第一个参数为回调函数(preState, action) => {return newState},根据上一个状态以及action返回新的状态,我们通常称这个函数为reducer,而action一般的规范是{type, ...rest},type用大写英文表示,rest为额外数据,
它与Array.prototype.reduce(callback, initValue)中的callback都是通过前一个状态以及触发改变的逻辑action获取下一个状态,当参数一致时,返回的状态也一致,所以reducer属于纯函数,所以不要添加副作用或者非纯函数在里面如API请求,Math.random()这些不确定因素
第二个参数非必选,传入用applyMiddleware包裹的中间件函数,对store的dispatch方法进行拦截加工,在执行dispatch触发状态更新前做一些额外的逻辑
import { createStore, applyMiddleware, combineReducers } from './index.js';
// 中间件redux-thunk:处理异步dispatch
import thunk from "./middlewares/thunk.js";
// 中间件redux-logger:打印state前后状态
import logger from "./middlewares/logger.js";
function reducer (state = 0, action) {
switch (action.type) {
case 'ADD': return ++state;
case 'MINUS': return --state;
default: return state;
}
}
const store = createStore(combineReducers({ count: reducer }), applyMiddleware(thunk, logger));
store.dispatch({ type: 'ADD' })
// 异步触发reducer,需要中间件对dispatch进行封装
store.dispatch((dispatch) => {
setTimeout(() => {
dispatch({type: "ADD", payload: 1});
}, 1000);
});
console.log(store.getState(), 888)
实现原理
createStore
- 当存在enhancer,也就是中间件的时候,会走中间件的逻辑
- 通过闭包将currentState存放到getState函数里
- dispatch接受action,触发reducer函数,根据不同的action获取不同的状态并发布订阅的函数
- 订阅函数listener存入数组并返回一个删除listener的函数
export default function createStore (reducer, enhancer) {
if (enhancer) {
return enhancer(createStore)(reducer)
}
let currentState;
const currentListeners = [];
function getState () {
return currentState;
}
function dispatch (action) {
currentState = reducer(currentState, action);
currentListeners.map((listener) => listener());
}
function subscribe (listener) {
currentListeners.push(listener);
return () => currentListeners.splice(currentListeners.indexOf(listener), 1);
}
return {
getState,
dispatch,
subscribe
}
}
applyMiddleware
- applyMiddleware将传入的中间件按顺序执行,通过聚合函数将他们包裹起来,形成Koa的那种洋葱圈模型,最后将封住后的dispath和其他参数返回
export default function applyMiddleware (...middlewares) {
return (createStore) => (reducer) => {
const store = createStore(reducer);
let dispatch = store.dispatch;
const midAPI = {
getState: store.getState,
dispatch
};
const chain = middlewares.map(middleware => middleware(midAPI))
dispatch = reverseCompose(chain)(store.dispatch);
return { ...store, dispatch }
}
}
function reverseCompose (fns) {
if (fns.length === 0) {
return (arg) => arg
}
if (fns.length === 1) {
return fns[0]
}
return fns.reduce((res, fn) => (...args) => res(fn(...args)))
}
combineReducers
- 对传过来的多个reducers的对象封装到一个大的reducer里,每次action进行比较,返回比较后的结果
export default function (reducers) {
return function combination (state = {}, action) {
let nextState = {};
let hasChanged = false;
for (let key in reducers) {
const reducer = reducers[key];
nextState[key] = reducer(state[key], action);
hasChanged = hasChanged || (nextState[key] !== state[key])
}
hasChanged = hasChanged || Object.keys(nextState).length !== Object.keys(state).length
return hasChanged ? nextState : state;
}
}
compose
- 这个不好解释,自己悟吧
function compose (fns) {
if (!fns.length) return () => {}
return fns.reduce((res, fn) => (...args) => fn(res(...args)));
}
bindActionCreators
- 这个是配合react-redux的connect用的,connect第二个参数可以接收函数对象
creaters,bindActionCreators就是对creaters进行封装,给对象的每个值添加dispatch的调用
function bindActionCreator (creater, dispatch) {
return (...args) => dispatch(creater(...args));
}
export default function (creaters, dispatch) {
return Object.keys(creaters).reduce((res, key) => {res[key] = bindActionCreator(creaters[key], dispatch); return res;}, {})
}
中间件的实现
处理异步的中间件
next为下一个中间件函数,当action为函数时,等到异步结束调用dispatch则执行下一个中间件
function thunk({dispatch, getState}) {
return next => action => {
if (typeof action === "function") {
return action(next, getState);
}
return next(action);
};
}