这是应该是基本系列的最后一节了. 上节使用了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
的开发环境工程就搭建完成了.