banner
SlhwSR

SlhwSR

热爱技术的一名全栈开发者
github
bilibili

Optimizing projects with redux-immutable

1. What is it#

Immutable, which means unchangeable, refers to data that cannot be changed once created in a computer.

Any modification, addition, or deletion operation on an Immutable object will return a new Immutable object.

The principle of Immutable implementation is Persistent Data Structure:

  • Use a data structure to store data.
  • When the data is modified, a new object is returned, but the new object will make full use of the previous data structure to avoid wasting memory.

In other words, when creating new data using old data, the old data must be available and unchanged. To avoid the performance loss caused by deepCopy copying all nodes, Immutable uses Structural Sharing.

If a node in the object tree changes, only this node and the parent nodes affected by it are modified, while other nodes are shared.

As shown in the figure below:

image

2. How to use#

The main library for using Immutable objects is immutable.js.

Immutable.js is a completely independent library that can be used regardless of the framework it is based on.

It addresses the problem of JavaScript not having immutable data structures and solves the performance problem through structural sharing.

It provides a complete set of Persistent Data Structures and many easy-to-use data types, such as Collection, List, Map, Set, Record, Seq, including:

  • List: An ordered index set, similar to an Array in JavaScript.

  • Map: An unordered index set, similar to an Object in JavaScript.

  • Set: A collection without duplicate values.

The main methods are as follows:

  • fromJS(): Converts a JavaScript data into an Immutable data.
const obj = Immutable.fromJS({a:'123',b:'234'})
  • toJS(): Converts an Immutable data into a JavaScript data.

  • is(): Compares two objects.

import { Map, is } from 'immutable'
const map1 = Map({ a: 1, b: 1, c: 1 })
const map2 = Map({ a: 1, b: 1, c: 1 })
map1 === map2   //false
Object.is(map1, map2) // false
is(map1, map2) // true
  • get(key): Retrieves the value of data or object.

  • getIn([]): Retrieves the value of a nested object or array. The parameter is an array representing the position.

let abs = Immutable.fromJS({a: {b:2}});
abs.getIn(['a', 'b']) // 2
abs.getIn(['a', 'c']) // The child does not have a value

let arr = Immutable.fromJS([1 ,2, 3, {a: 5}]);
arr.getIn([3, 'a']); // 5
arr.getIn([3, 'c']); // The child does not have a value

The following example shows how to use the methods:

import Immutable from 'immutable';
foo = Immutable.fromJS({a: {b: 1}});
bar = foo.setIn(['a', 'b'], 2);   // Use setIn to assign a value
console.log(foo.getIn(['a', 'b']));  // Use getIn to retrieve a value, prints 1
console.log(foo === bar);  //  Prints false

If using native js, it would be as follows:

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

3. Application in React#

Using Immutable can optimize the performance of React applications, mainly by reducing the number of renders.

When optimizing react performance, to avoid redundant rendering, we compare in shouldComponentUpdate() and execute the render method when returning true.

Immutable can complete the comparison through the is method, without the need for deep comparison.

In redux, Immutable can also be combined to avoid the need for deep copying when modifying data before using Immutable.

import '_' from 'lodash';

const Component = React.createClass({
  getInitialState() {
    return {
      data: { times: 0 }
    }
  },
  handleAdd() {
    let data = _.cloneDeep(this.state.data);
    data.times = data.times + 1;
    this.setState({ data: data });
  }
}

After using Immutable:

getInitialState() {
  return {
    data: Map({ times: 0 })
  }
},
  handleAdd() {
    this.setState({ data: this.state.data.update('times', v => v + 1) });
    // The value of times will not change at this point
    console.log(this.state.data.get('times'));
  }

Similarly, in redux, the data can also be processed with fromJS.

import * as constants from './constants'
import {fromJS} from 'immutable'
const defaultState = fromJS({ // Convert data to immutable data
    home:true,
    focused:false,
    mouseIn:false,
    list:[],
    page:1,
    totalPage:1
})
export default(state=defaultState,action)=>{
    switch(action.type){
        case constants.SEARCH_FOCUS:
            return state.set('focused',true) // Change immutable data
        case constants.CHANGE_HOME_ACTIVE:
            return state.set('home',action.value)
        case constants.SEARCH_BLUR:
            return state.set('focused',false)
        case constants.CHANGE_LIST:
            // return state.set('list',action.data).set('totalPage',action.totalPage)
            // merge is more efficient, changing multiple data at once
            return state.merge({
                list:action.data,
                totalPage:action.totalPage
            })
        case constants.MOUSE_ENTER:
            return state.set('mouseIn',true)
        case constants.MOUSE_LEAVE:
            return state.set('mouseIn',false)
        case constants.CHANGE_PAGE:
            return state.set('page',action.page)
        default:
            return state
    }
}
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.