闲来无事,将todos重写了下,主要有以下几个改进的地方:
- 改造
bindActionCreators
方法,使其支持定义组件树状结构的action创建函数,目的是方便分发和后期逻辑扩展; - 引入
createReducer
方法,将switch case
结构的action
分发改为属性索引,避免一个函数过长。并根据state
结构来定义rootHandler
,将子hanlder放在对应的state
节点之下。 - 定义
sliceReucers
方法,避免了手动调用createReducer
- 引入
immutable
,使用不可变数据和结构共享 - 引入
reducerEnhancer
,在原始reducer的基础上实现扩展,这个新的 reducer可以处理新的 action,或者维护更多的 state,亦或者将它无法处理的 action 委托给原始的 reducer 处理。本栗子中主要用来处理undo
和redo
操作
然后明确几个概念:
- 将reducer拆分, 拆分后的reducer只负责管理全局 state 中它负责的一部分。每个 reducer 的 state 参数都不同,分别对应它管理的那部分 state 数据
- 每个
reducer
可能对应着不同的action
,对state
里该节点的数据做不同的操作。例如todosRecuder
内有addTodo
deleteTodo
等等;
看下文件的结构划分:
component
:主要存放react组件action
:定义action常量
以及树状的action创建函数
reducer
:定义initialState
rootHandler
(即action处理逻辑)子reducer
,并对外输出组合后的reducer
- reducerEnhancer : 定义函数
reducer enhancer
(或者higher order reducer
),完成rootHanlder
之外的action
,例如UNDO
REDO
store
:依据reducer创建store,引入中间件等等utils
:state工具函数main
:引入provider 和store , 输出App
mapActionCreators
使用该方法代替默认的bindActionCreators
方法,并在connect
时使用,便可以依据组件结构来定义actionCreators
下面是具体的代码:
export const mapActionCreators= (acObject,dispatch) => {
// make a copy of the tree ,in case of monipulate
let result = {};
const looper = function (obj, result) {
forEach(obj, function (val, key) {
if (typeof (val) === 'object') {
result[key] = {};
looper(val, result[key]);
}
if (typeof (val) === 'function') result[key] = function () {
dispatch(val.apply(undefined,arguments))
}
})
};
looper(acObject, result);
return result;
}
function forEach(target, cb) {
if (Array.isArray(target)) {
for (let value of target) {
cb(value);
}
} else {
let keysArr = Object.keys(target);
for (let key of keysArr) {
cb(target[key], key);
}
}
}
定义actionCreators
依据组件的结构定义actionCreators,如下:
const actionCreators = {
undo:()=>{
return {type:UNDO}
},
redo:()=>{
return {type:REDO}
},
acHeader:{
addTodo : (item)=>{
return {type:ADD_TODO , payload:{text:item,isDone:false}}
}
},
acTodoList:{
acTodoItem:{
deleteTodo:(index)=>{
return {type : DELETE_TODO , index}
},
toggleStatus:(index,status)=>{
return {type:TOGGLE_STATUS,index,status}
}
}
},
acFooter:{
setVisibleFilter:(filter)=>{
return {type:SET_VISIBLE_FILTER,filter}
}
}
}
分发actionCreators
在connect之后,在组件内使用...
分发actionCreators,如下:
class Todos extends Component{
constructor(p){
super(p)
}
render(){
let {acHeader,acTodoList,acFooter,todos,undo,redo} = this.props;
return <div className='todos-wrapper'>
<button onClick={undo}>undo</button>
<button onClick={redo}>redo</button>
<Header {...acHeader} />
<TodoList {...acTodoList} todos={todos.toJS()} />
<Footer {...acFooter} />
</div>
}
}
const ConnectTodos = connect(
state =>({
todos:state.get('present').todos,
visibleFilter:state.visibleFilter
}),
dispath => mapActionCreators(actionCreators,dispath)
)(Todos)
reducerEnhancer
reducer enhancer(或者 higher order reducer)作为一个函数,接收 reducer 作为参数并返回一个新的 reducer,这个新的 reducer 可以处理新的 action,或者维护更多的 state,亦或者将它无法处理的 action 委托给原始的 reducer 处理。这不是什么新模式, combineReducers()也是 reducer enhancer,因为它同样接收多个 reducer 并返回一个新的 reducer。
for more information
以下是实现撤销重做的reducerEnhancer
/** 增强reducer,可以用来完成撤销和重做
* reducer enhancer(或者 higher order reducer)作为一个函数,接收 reducer 作为参数并返回一个新的 reducer,这个新的 reducer 可以处理新的 action,或者维护更多的 state,亦或者将它无法处理的 action 委托给原始的 reducer 处理。这不是什么新模式, combineReducers()也是 reducer enhancer,因为它同样接收多个 reducer 并返回一个新的 reducer。
* **/
import {fromJS} from 'immutable'
export default (reducer) => {
// 以一个空的 action 调用 reducer 来产生初始的 state
const initialState =fromJS({
past: [],
present:reducer(undefined,{}),//初始化当前的状态,然后放在past队列中
future: []
}
)
// 返回一个可以执行撤销和重做的新的reducer
return function (state = initialState, action) {
switch (action.type) {
case 'UNDO':
if(state.get('past').size == 0) return state;
return state
.update('future', future => {
return future.unshift(state.get('present'));
})
.set('present', state.get('past').last())
.update('past', past => {
return past.pop();
});
case 'REDO':
if(state.get('future').size == 0) return state;
return state
.update('past', past => {
return past.push(state.get('present'));
})
.set('present', state.get('future').first())
.update('future', future => {
return future.shift();
});
default:
// 将其他 action 委托给原始的 reducer 处理
let present = state.get('present');
let newPresent = reducer(present, action);
if (present === newPresent) return state;
return state
.update('past', past => past.push(present))
.set('present', newPresent)
.update('future', future => {
return future.clear();
})
}
}
}
reducer
import {ADD_TODO, DELETE_TODO, SET_VISIBLE_FILTER,TOGGLE_STATUS} from './action'
import {DONE,UNDO,ALL} from '../constants'
import {sliceReducers,createReducer} from './utils'
import {combineReducers} from 'redux'
import {fromJS,Map} from 'immutable'
/**定义初始state**/
export const initialState =fromJS(
{
todos:[],
visibleFilter:ALL
}
)
/**根hanlder**/
const rootHandlers = {
todos:{
[ADD_TODO] :(state,action) =>{
return state.push(Map(action.payload))
},
[DELETE_TODO] : (state,action) =>{
return state.delete(action.index)
},
[TOGGLE_STATUS] :(state,action)=>{
return state.setIn([action.index,'isDone'],action.status)
}
},
visibleFilter:{
[SET_VISIBLE_FILTER] :(state,action) =>{
return state
}
}
}
/**slice reducer**/
const reducers = sliceReducers(initialState,rootHandlers)
export default (state = initialState,action)=>{//在这里指定初始state
return state.set('todos',reducers.todos(state.get('todos'),action))
.set('visibleFilter',reducers.visibleFilter(state.get('visibleFilter'),action))
}
sliceReducers && createReducers
const createReducer = (initialState,handlers)=>{
return (state = initialState, action) => {
if(handlers.hasOwnProperty(action.type)) return handlers[action.type](state,action);
return state ;
}
}
/**
* sliceReducers 仅初始化 的时候执行 一次,调用此方法会为rootHandlers上的每个节点调用一次createReducer
* @param:
* initialState:初始化state
* rootHandlers:state上每个节点对应的处理逻辑的集合
* */
export const sliceReducers = (initialState,rootHandlers) =>{
let reducers = {};
for (let key in rootHandlers){
reducers[key] = createReducer(initialState.get(key),rootHandlers[key]);
}
return reducers;
}
这仅列举了几个重要的文件代码,项目源码放在了Github上