闲来无事,将todos重写了下,主要有以下几个改进的地方:

  • 改造bindActionCreators 方法,使其支持定义组件树状结构的action创建函数,目的是方便分发和后期逻辑扩展;
  • 引入createReducer 方法,将switch case 结构的action分发改为属性索引,避免一个函数过长。并根据state 结构来定义rootHandler ,将子hanlder放在对应的state 节点之下。
  • 定义sliceReucers 方法,避免了手动调用createReducer
  • 引入immutable ,使用不可变数据和结构共享
  • 引入reducerEnhancer,在原始reducer的基础上实现扩展,这个新的 reducer可以处理新的 action,或者维护更多的 state,亦或者将它无法处理的 action 委托给原始的 reducer 处理。本栗子中主要用来处理undoredo 操作

然后明确几个概念:

  • 将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


本文转载:CSDN博客