使用Immutable.js更新Redux应用——简单方法
背景
复杂 Redux 应用的 State 可能会有很深的层次,比如
const initial_state = {
status: {
is_fetching: false,
last_updated: null
},
environment: {
repo_list: [
{ owner: 'nwp_xp', repo: 'nwpc_op' },
{ owner: 'nwp_xp', repo: 'nwpc_qu' }
],
config_file_path: ''
},
setup_env: {
status: 'unknown'
},
load_log: {
repos: {}
}
}
reducer 在修改 state 时,需要生成一个新的对象,而一种原生 JavaScript 复制对象的方法是使用 Object.assign()
生成新的对象,但 Object.assign
只能修改对象第一层次的属性。
想要修改更深层次的属性,可以对层次树上的所有对象使用 Obejct.assign
,比如在上述 state 中修改 load_log.repos
的属性
(state, payload) => {
let load_log = state.load_log;
let repos = load_log.repos;
let key = payload.key;
let new_repo = {};
if(key in repos){
new_repo = Object.assign({}, repos[key], payload.value);
} else {
new_repo = Object.assign({}, payload.value);
}
let new_repos = Object.assign({}, repos);
new_repos[key] = new_repo;
let new_load_log = Object.assign({}, load_log, {
repos: new_repos
});
let new_state = Object.assign({}, state, {
load_log: new_load_log
});
return new_state;
}
上述是我能想到的方式,过于繁琐,缺乏美感,并且编写容易出错。
Redux 可以采用多级 reducer 处理 state,每级 reducer 只处理该层次的属性,相当于将每个 Object.assign
放到不同层次的函数中。
尽管解决层次问题,但很多时候不需要创建那么多层次的 reducer,仍然需要在单一 reducer 中直接修改 state。
对于 Redux 中的 state,每次 state变化都会生成一个新的 state 对象,这和 Immutable.js 库提供的不可变对象很相似,所以本文尝试将 Redux 与 Immutable.js 结合。
方法
参考《Updating your Redux app to use ImmutableJS》
对于复杂的 redux 应用,将 state 全部修改为 Immutable 类型改动量太大。 reducer 最需要解决的问题就是如何从旧 state 根据修改条件生成新 state 的问题。
修改 Immutable 对象只能得到对象的克隆,并且 Immutable 提供大量修改克隆对象的方法。 因此最简单的方式就是在 reducer 方法中利用 Immutable 类型的特点,改写现有的代码。
实现
reducer 函数中现将 state 使用 Immutable.fromJS
方法转为 Immutable
对象,生成新的 state 对象后,再使用 toJS
方法转为 JavaScript 对象。
修改上述的 reducer 函数:
(state, payload) => {
state = Immutable.fromJS(state);
let repo_object = Immutable.Map();
repo_object = repo_object.set(payload.key, payload.value);
const new_state = state.updateIn(['load_log', 'repos'],
repo_map => repo_map.mergeDeep(repo_object)).toJS();
return new_state;
}
可以看到,使用 Immutable.js 后,代码更加清晰。
后续
后续考虑将整个 state 替换为 Immutable 对象,但这需要修改更多的代码。