使用 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对象,但这需要修改更多的代码。