这是应该是基本系列的最后一节了. 上节使用了redux, 在redux的reducer里面的返回值应该是一个新的state对象, 而不是在旧的state对象上进行修改, 那么在ES6语法中, 就需要写很多{...state, ...newState}这种代码来创建新对象. 而且如果改变的值的所在对象层次比较深, 那么这样的代码写起来就很痛苦了. 为此, Facebook专门创建了一个库用于解决这种问题 - immutable.
引入immutable后, 整个应用的state应该都变为immutable类型, 那么在createStore时就需要修改一下初始值.
1
| const store = createStore(RootReducer, Immutable.Map(), applyMiddleware(...middleware));
|
然后, 在combineReducers时候, 原来的state是直接使用=赋值替换的, 整个state变成immutable后, 语法也应该变成immutable提供的set方法, 那么combineReducers就需要重写了. 不过幸运的是, 已经有人把这项工作做好了. 使用redux-immutable包提供的combineReducers来替换原来redux提供的combineReducers.
1 2 3
| //import { combineReducers } from 'redux'; import { combineReducers } from 'redux-immutable'; const RootReducer = combineReducers({...});
|
这两项工作做好以后, 我们就可以在reducer和component里使用immutable的数据结构了.
1 2 3 4 5 6 7 8
| const reducer = (state = defaultState, action) => { switch (action.type) { case ActionType.ADD_TODO: return state.update('value', (v) => (v + action.payload)); default: return state; } }
|
然后需要把router也整合进来, 构成react-router, react-redux, immutable的最佳开发环境. router本身就提供结合redux的方法, 需要自己改动的并不多. 首先需要改造我们的store创建过程.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { createStore, applyMiddleware } from 'redux'; import ReduxThunk from 'redux-thunk'; import { routerMiddleware } from 'react-router-redux'; import Immutable from 'immutable'; import { createLogger } from 'redux-logger'; import { createBrowserHistory } from 'history';
import RootReducer from './../_reducer';
let history = createBrowserHistory();
const middleware = [routerMiddleware(history), ReduxThunk];
const initialState = Immutable.Map(); const store = createStore(RootReducer, initialState, applyMiddleware(...middleware));
export default store; export { history };
|
这样创建的store就已经支持了router, 然后我们再加入router的reducer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import Immutable from 'immutable'; import { LOCATION_CHANGE, } from 'react-router-redux'; const initialState = Immutable.fromJS({ location: null, }); export default function routerReducer(state = initialState, action) { if (action.type === LOCATION_CHANGE) { return state.set('location', Immutable.fromJS(action.payload)); } return state; }; export const reducerName = 'routing';
import { combineReducers } from 'redux-immutable'; import routerReducer, { reducerName as routerReducerName } from './../route/route-reducer'; const RootReducer = combineReducers({ [routerReducerName] : routerReducer, ..., }); export default RootReducer;
|
这样, router便与immutable能结合使用了.
我们再给我们的store做点增强.
redux-logger是一个很常见的中间件, 可以为我们的每个action提供日志记录, 我们也将它加入.
1 2 3 4 5
| import logger from 'redux-logger'; if (process.env.NODE_ENV === 'development') { middleware.push(logger); }
|
但是这样生成的log在控制台显示的全是immutable对象, 并不是直接的js对象, 不方便展开查看. 去看redux-logger源码, 发现它也提供一个createLogger方法来让我们自己创建自己的logger.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| const defaultLogger = ({ dispatch, getState } = {}) => { if (typeof dispatch === 'function' || typeof getState === 'function') { return createLogger()({ dispatch, getState }); } }; export { defaults, createLogger, defaultLogger as logger }; export default defaultLogger;
function createLogger(options = {}) { const loggerOptions = Object.assign({}, defaults, options); ... }
import { createLogger } from 'redux-logger'; if (process.env.NODE_ENV === 'development') { middleware.push(({ dispatch, getState }) => { return createLogger({ collapsed: true })({ dispatch, getState: () => { return getState().toJS(); } }); }); }
import { Iterable } from 'immutable'; const stateTransformer = (state) => { if (Iterable.isIterable(state)) return state.toJS(); else return state; }; const logger = createLogger({ stateTransformer, });
|
到目前为止, 一个react + react-router + react-redux + immutable的开发环境工程就搭建完成了.