react 4 - immutable

这是应该是基本系列的最后一节了. 上节使用了redux, 在reduxreducer里面的返回值应该是一个新的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({...});

这两项工作做好以后, 我们就可以在reducercomponent里使用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'; // 所有的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, 然后我们再加入routerreducer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//router-reducer.js
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';
//reducer.js
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') {
//在开发环境下可以看到log
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;
//createLogger
function createLogger(options = {}) {
const loggerOptions = Object.assign({}, defaults, options);
...
}
//改造logger, 其实等于创建一个自己的中间件
import { createLogger } from 'redux-logger';
if (process.env.NODE_ENV === 'development') { //在开发环境下可以看到log
middleware.push(({ dispatch, getState }) => {
return createLogger({ collapsed: true })({//collapsed还可以是个action
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的开发环境工程就搭建完成了.

作者

Mosby

发布于

2017-08-04

许可协议

评论