简介
Redux的核心API有combineReducers, createStore, applyMiddleware, bindActionCreators、compose,bindActionCreators目前没实现,稍后补上。
先附上actions和actionType
// actionType/constant.js
export const ADD_TODO = 'ADD_TODO';
export const MODIFY_TODO = 'MODIFY_TODO';
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';
// actions/todo.js
import * as constant from '../actionType/constant';
export default {
addTodo: text => {
return {
type: constant.ADD_TODO,
id: parseInt(Math.random().toString().slice(-5)),
text
}
},
modifyTodo: (id, text) => {
return {
type: constant.MODIFY_TODO,
id,
text
}
},
};
// actions/index.js
import todo from './todo';
import visiblilityFilter from './visiblilityFilter';
export default {
todo,
visiblilityFilter,
}
createStore
createStore就是创建store的函数,第一个参数reduce必传,initalState和enhance可选,这里是简单实现,没做错误处理,实际上当第二个参数是函数时会把它当做enhance,而initalState是undefined,具体可参 Redux-createStore 82-85行 。
createStore返回一个对象,有几个属性getState(获取state)、dispatch(派发action,reducer更新state)、subscribe(将订阅的回调加入listener列表,每次reducer更新state后会执行listener中的回调),他们是函数。
bug:自己实现的createStore,不传initalState,state默认空对象,而Redux源码中,即使不传initalState,createStore后得到的store不是空对象。需要fix。
// lib/redux/createStore
export default function createStore(reducer, initalState, enhance) {
let state = initalState || {},
listeners = []
const getState = () => {
return state
}
const dispatch = (action) => {
state = reducer(state, action)
listeners.forEach(fn => fn())
}
const subscribe = (fn) => {
listeners.push(fn)
return () => {
unsubscribe(fn)
}
}
const unsubscribe = (listener) => {
let index = listeners.indexOf(listener)
if (index === -1) {
listeners.splice(index, 1)
}
}
return {
getState,
dispatch,
subscribe,
}
}
combineReducers
将多个reducer合并成一个,有点像Object.assign,下面的key可以理解为子模块的标识
// lib/redux/combineReducers.js
/* reducerObj相当于
* {
* todos,
* visibilityFilter,
* }
*/
export default function combineReducers(reducerObj) {
return function (state = {}, action) {
const keys = Object.keys(reducerObj);
const newState = {}
keys.forEach(key => {
// key相当于todos,reducer相当于reducers/todos.js中的reducer
const reducer = reducerObj[key]
newState[key] = reducer(state[key], action)
})
return {
...state,
...newState
}
}
}
// reducers/index.js
// import { combineReducers } from 'redux'
import { combineReducers } from '../lib/redux'
import todos from './todos'
import visibilityFilter from './visibilityFilter'
const todoApp = combineReducers({
todos,
visibilityFilter,
})
export default todoApp
createReducer
为了解决reducer中Switch分支而产生,接受initialState和handlers对象,返回一个reducer
// lib/redux/createReducer
const createReducer = (initialState, handlers) => {
return (state = initialState, action) => {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action);
}
return state;
};
};
export default createReducer;
applyMiddleware
为了记录派发action、执行reducer前后state,可以用middleware来增强dispatch(为什么是dispatch?因为action就是纯对象,reducer是纯函数,subscribe就是添加订阅回调到队列,没法搞事情啊!!!)。增强dispatch,可以修改dispatch,比如每次dispatch打印action。
const __dispatch = store.dispatch
store.dispatch = (action) => {
console.log('action: ', action);
__dispatch(action);
}
想想每次使用middleware都要保存旧的dispatch再重写,不太方便,先将middleware重新定义如下:
const middleware = (store) => (dispatch) => (action) => {
// do what you want
dispatch(action);
// do what you want
};
两个简单的middleware:
// 只打印出 Action
const loggerAction = (store) => (dispatch) => (action) => {
console.log('action: ', action);
dispatch(action);
};
// 只打印出 更新后的state
const loggerState = (store) => (dispatch) => (action) => {
console.log('current state: ', store.getState());
dispatch(action);
console.log('next state: ', store.getState());
};
现在来实现applyMiddleware。applyMiddleware就是将多个middleware串起来,forEach就搞定了。
PS:applyMiddleware有不同形式,这里只实现了 store = applyMiddleware(store, funcArr)
// lib/redux/applyMiddleware
const applyMiddleware = (store, middlewares) => {
let dispatch = store.dispatch;
middlewares.forEach((middleware) => {
dispatch = middleware(store)(dispatch);
});
return {
...store,
dispatch,
};
};
export default applyMiddleware;
// src/todoDemo/index.js
// 每次dispatch会先打印current state 和 action,执行reducer后打印next state,参考Koa洋葱圈模型
store = applyMiddleware(store, [loggerAction, loggerState]);
compose
给定函数列表,将多个函数有序执行
// lib/redux/compose
const compose = (...funcArr) => {
return funcArr.reduce((a, b) => (...args) => a(b(...args)));
};
export default compose;