宗旨,就是为了解决状态难以管理,给出统一的管理方式,使得状态更加追踪、维护

首先,我们基于上面的流程,先创建一个 Store,用来存储 State
// createStore.js
export default function createStore(reducer, initState) {
let state = initState;
let listeners = [];
function subscribe(listener) {
listeners.push(listener);
};
function getState() {
return state;
}
function dispatch(action) {
state = reducer(state, action);
for (let i = 0; i < listeners.length; i++) {
listeners[i]();
}
}
return {
subscribe,
getState,
dispatch,
}
}
可以看到,核心就是一个发布订阅,接着我们去定义简单的
// counter.js
let initState = {
count: 0
}
export default function counterReducer(state, action) {
if (!state) {
state = initState;
}
switch (action.type) {
case 'changeCount':
return {
...state,
count: action.payload.count
}
default:
return state;
}
}
Reducer 比较简单,我们只需要去判断 Reducer 的场景,然后去更新 state,接着,我们需要去思考,我们的应用不止一个 State,如果都将业务逻辑写到一个 Reducer 中,改变一个 State,可能造成整个 State 的变更,为了解决这个问题,我们要将 Reducer 组合起来
// combineReducers.js
export default function combineReducer(reducers) {
const reducerKeys = Object.keys(reducers);
// 合并reducers,形成新的reducer
return function combine(state = {}, action) {
const nextState = {};
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];
const reducer = reducers[key];
const prevState = state[key];
const nextStateForKey = reducer(prevState, action);
nextState[key] = nextStateForKey;
}
return nextState;
}
}
// useage
// const reducer = combineReducers({
// info,
// counter
// })
在 combineReducers 中,我们将我要要合并的 Reducer 对象,返回新的 Reducer 方法,执行每个 Reducer,返回的 State,这样,我们就实现了多个 Reducer 的合并,可以拆分我们的业务逻辑,接着,我们看 Redux 中间件如何开发
// loggerMiddleware.js
const loggerMiddleware = (store) => (dispatch) => (action) => {
console.log('state', store.getState());
try {
dispatch(action);
console.log('action', action);
} catch (e) {
console.error('Error=>', e)
}
console.log('next state', store.getState());
}
export default loggerMiddleware;
middleWare 其实就是一个柯里化函数,目的就是增强 Store 能力,扩展 dispatch 的能力,比如我们可以通过 middleWare 拦截 dispatch 和 action,做一些同步异步的处理、状态监听等等操作,middleWare 有了,我们要如何赋予到 Store 上呢,那就是 applyMiddleware
// applyMiddleware.js
import compose from './compose.js'
const applyMiddleware = function (...middlewares) {
return function (Store) {
return function (reducer, initialState) {
const store = Store(reducer, initialState); // createStore();
const state = { getState: store.getState };
const chain = middlewares.map((middleware) => {
return middleware(state) // 我们这里只需要store中的getState,当然,我们也可以将store传入
});
const dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
}
}
}
}
export default applyMiddleware;
现在,applyMiddleware 要实现上面的柯里化函数,首先我们需要 store 和 dispatch,我们可以利用 createStore,来获取我们要用到的 store 和 dispatch,但是发现 applyMiddleware 也是一个柯里化函数,我们怎么执行呢,那么就要在 createStore 中去判断,我们的目的就是扩展 dispatch,所以修改下 createStore.js
export default function createStore(reducer, initState, newDispatch) {
// 传递Store,返回一个新的Store
if (newDispatch) {
const newCreateStore = newDispatch(createStore);
return newCreateStore(reducer, initState);
}
let state = initState;
let listeners = [];
function subscribe(listener) {
listeners.push(listener);
};
function getState() {
return state;
}
function dispatch(action) {
state = reducer(state, action);
for (let i = 0; i < listeners.length; i++) {
listeners[i]();
}
}
function replaceReducer(nextReducer) {
reducer = nextReducer;
}
return {
subscribe,
getState,
dispatch,
replaceReducer
}
}
如果有 middleware 需要组合,那么我们就重载掉之前的 dispatch,并传入我们需要的 Reducer 和 state,但是目前多个 middleware 还未解决,我们看下 componse 函数
// componse.js
export default function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((a, b) => {
return (...args) => a(b(...args))
})
}
我们可以看到,componse 就是组合函数,通过 reduce 实现,类似 Koa 的洋葱模型,组合函数,但是我们的函数将自上而下执行
// 目前我们的middleware是一个数组,虽然传入了store,但是是一个Function的Array
// (next)=>(action)=>{}
// (next)=>(action)=>{}
// (next)=>(action)=>{}
// (next)=>(action)=>{}
/**
* 如果我们使用了函数组合,就是下面这样的,因为我们的middleware都需要dispatch和action,而且返回值也是diapatch和action
*/
(next)=>(action)=>{
(next)=>(action)=>{
(next)=>(action)=>{
}
}
}
上面,我们已经实现了一个简单的 Redux,接着我们去扩展 action,让我们的 action 通过反柯里化的方式,更加易用,那么就是我们要实现的 bindActionCreators
// bindActionCreators.js
function boundActionCreator(actionCreator, dispatch) {
return function () {
return dispatch(actionCreator.apply(this, arguments));
}
}
export default function bindActionCreators(bindActionCreators, dispatch) {
const boundActionCreators = {};
for (const key in bindActionCreators) {
const actionCreator = bindActionCreators[key];
if (typeof actionCreator === 'function') {
boundActionCreators[key] = boundActionCreator(actionCreator, dispatch);
}
}
return boundActionCreators;
}
我们通过反柯里化的方式,将待执行函数挂载到对象上,更加明确我们的业务逻辑,简单看下用法
// usage
const actions = bindActionCreators({
setCounter,
setInfo,
}, store.dispatch);
actions.setCounter();
actions.setInfo();
// counter.js
const setCounter = () => {
return {
count: 99
}
}
export {
setCounter
}
// info.js
const setInfo = () => {
return {
name: '🍊 duanxinlei action',
description: '🍊 duanxl action desc'
}
}
export {
setInfo
}