Immutable 在 React 中的应用

理解 Immutable 数据结构及其在 React 项目中的使用方式

问题

如何理解 Immutable?如何在 React 项目中应用?

解答

什么是 Immutable

Immutable 指一旦创建就不能被更改的数据。对 Immutable 对象的任何修改、添加或删除操作都会返回一个新的 Immutable 对象。

Immutable 基于 Persistent Data Structure(持久化数据结构)实现。当数据被修改时,会返回一个新对象,但新对象会尽可能复用之前的数据结构,避免内存浪费。

通过 Structural Sharing(结构共享),当对象树中某个节点发生变化时,只修改该节点和受影响的父节点,其他节点保持共享。

使用 immutable.js

immutable.js 提供了完整的不可变数据结构,主要数据类型包括:

  • List:有序索引集,类似 Array
  • Map:无序索引集,类似 Object
  • Set:无重复值的集合

常用方法:

import Immutable, { Map, is } from 'immutable';

// fromJS:将 JS 数据转换为 Immutable 类型
const obj = Immutable.fromJS({ a: '123', b: '234' });

// toJS:将 Immutable 数据转换为 JS 类型
const jsObj = obj.toJS();

// is:比较两个对象
const map1 = Map({ a: 1, b: 1, c: 1 });
const map2 = Map({ a: 1, b: 1, c: 1 });
map1 === map2;  // false
is(map1, map2); // true

// get:取值
obj.get('a');

// getIn:嵌套取值
let abs = Immutable.fromJS({ a: { b: 2 } });
abs.getIn(['a', 'b']); // 2

// setIn:嵌套赋值
let foo = Immutable.fromJS({ a: { b: 1 } });
let bar = foo.setIn(['a', 'b'], 2);
console.log(foo.getIn(['a', 'b'])); // 1
console.log(foo === bar); // false

对比原生 JS:

let foo = { a: { b: 1 } };
let bar = foo;
bar.a.b = 2;
console.log(foo.a.b); // 2
console.log(foo === bar); // true

在 React 中应用

性能优化

shouldComponentUpdate 中使用 is 方法进行对比,避免深度比较:

import { is } from 'immutable';

shouldComponentUpdate(nextProps, nextState) {
  return !is(this.props.data, nextProps.data);
}

组件状态管理

使用 Immutable 前需要深拷贝:

import _ from 'lodash';

handleAdd() {
  let data = _.cloneDeep(this.state.data);
  data.times = data.times + 1;
  this.setState({ data: data });
}

使用 Immutable 后:

getInitialState() {
  return {
    data: Map({ times: 0 })
  };
}

handleAdd() {
  this.setState({ 
    data: this.state.data.update('times', v => v + 1) 
  });
  console.log(this.state.data.get('times')); // 不会改变
}

结合 Redux

import { fromJS } from 'immutable';

const defaultState = fromJS({
  home: true,
  focused: false
});

function reducer(state = defaultState, action) {
  switch (action.type) {
    case 'UPDATE_FOCUSED':
      return state.set('focused', action.value);
    default:
      return state;
  }
}

关键点

  • Immutable 通过结构共享实现高性能的不可变数据,修改时只复制变化的节点
  • 使用 is 方法进行对象比较,避免深度比较带来的性能损耗
  • 在 React 中可优化 shouldComponentUpdate,减少不必要的渲染
  • 结合 Redux 使用时,用 fromJS 转换初始状态,用 setupdate 等方法更新数据
  • 主要 API:fromJStoJSisgetgetInsetsetInupdate