Immutable JS for React rendering optimization (2)

Keywords: React Javascript Attribute

In the previous section, we implemented a simple performance optimization, but because we need a deep copy and property comparison, the performance is still poor, so we use immutable.js written by Facebook God for further optimization

Immutable collection of immutable.js JavaScript

Each modification will return a new object, where the previous object will always remain. It provides some common data types for us to use. The common data types are as follows:

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

Here are only two most commonly used data types. If you want to know more types, you can go to immutabl official website see

  • Simple approach
let immutable = require('immutable')
let {Map, List} = immutable


let obj1 = Map({name: 'leo', age: 12)
console.log(obj1) // Map { "name": "leo", "age": 12 }
                
let obj2 = obj1.set('name', 'hello')
// For simple use, the returned object is different from the previous object, as shown in the following case
console.log(obj2) // Map { "name": "hello", "age": 12 }
console.log(obj1 === obj2) // false

  • Properties will be shared
let immutable = require('immutable')
let {Map, List, fromJS} = immutable

let obj1 = fromJS({ name: 'leo', age: 12, home: { name: 'shenzhen' } })
let obj2 = obj1.set('name', 'hello')

// Properties will be shared
console.log(obj1.home === obj2.home) // true
// Get the number of properties of the object
console.log(obj1.size) // 3
console.log(obj1.count()) // 3
  • Pain points of map

Although the map is easy to use, the map can only execute one layer. We will report an error for the level 2 update attribute. We need to use fromjs to process the object

Set value set is used for one layer and setIn is used for multiple layers

/Value: get is used at one level and getIN is used at multiple levels

let immutable = require('immutable')
let {Map, fromJS} = immutable

let obj1 = fromJS({ name: 'leo', age: 12, home: { name: 'shenzhen' } })
let obj2 = obj1.set('name', 'hello')
let obj3 = obj1.setIn(['home', 'name'], 'new Name')

console.log(obj3) // Map { "name": "leo", "age": 12, "home": Map { "name": "new Name" } }
console.log(obj3.getIn(['home', 'name'])) // new Name
  • update with new value
let immutable = require('immutable')
let {Map, fromJS} = immutable

let obj1 = fromJS({ name: 'leo', age: 12, home: { name: 'shenzhen' } })
let obj2 = obj1.set('name', 'hello')
let obj3 = obj1.setIn(['home', 'name'], 'new Name')
let obj4 = obj3.update('age', val => val + 1)

console.log(obj3) // Map { "name": "leo", "age": 12, "home": Map { "name": "new Name" } }
console.log(obj3.getIn(['home', 'name'])) // new Name
console.log(obj4) // Map { "name": "leo", "age": 13, "home": Map { "name": "new Name" } }

  • Delete a property
let immutable = require('immutable')
let {Map, fromJS} = immutable

let obj1 = fromJS({ name: 'leo', age: 12, home: { name: 'shenzhen' } })
let obj2 = obj1.set('name', 'hello')
let obj3 = obj1.setIn(['home', 'name'], 'new Name')
let obj4 = obj3.update('age', val => val + 1)
let obj5 = obj4.delete('age')

console.log(obj5) // Map { "name": "leo", "home": Map { "name": "new Name" } }
  • Empty object
let immutable = require('immutable')
let {Map, fromJS} = immutable

let obj1 = fromJS({ name: 'leo', age: 12, home: { name: 'shenzhen' } })
let obj2 = obj1.set('name', 'hello')
let obj3 = obj1.setIn(['home', 'name'], 'new Name')
let obj4 = obj3.update('age', val => val + 1)
let obj5 = obj4.delete('age')
let obj6 = obj4.clear()

console.log(obj6) // Map {}
  • merge object
let immutable = require('immutable')
let {Map, fromJS} = immutable

let obj1 = fromJS({ name: 'leo', age: 12, home: { name: 'shenzhen' } })
let obj2 = obj1.set('name', 'hello')
let obj3 = obj1.setIn(['home', 'name'], 'new Name')
let obj4 = obj3.update('age', val => val + 1)
let obj5 = obj4.delete('age')
let obj6 = obj4.clear()
let obj7 = obj6.merge({name: 'world', gender: 'man'})

console.log(obj7) // Map { "name": "world", "gender": "man" }
  • Type conversion
let immutable = require('immutable')
let {Map, fromJS} = immutable

let obj1 = fromJS({ name: 'leo', age: 12, home: { name: 'shenzhen' } })
let obj2 = obj1.set('name', 'hello')
let obj3 = obj1.setIn(['home', 'name'], 'new Name')
let obj4 = obj3.update('age', val => val + 1)
let obj5 = obj4.delete('age')
let obj6 = obj4.clear()
let obj7 = obj6.merge({name: 'world', gender: 'man'})

console.log(obj7.toJS()) // { name: 'world', gender: 'man' }
console.log(obj7.toJSON()) // { name: 'world', gender: 'man' }
console.log(obj7.toObject()) // { name: 'world', gender: 'man' }

Simple use of immutable.js list data type

Operation method is similar to array

let immutable = require('immutable')
let { Map, List, fromJS } = immutable

let arr1 = List([1, 2, 3])
let arr2 = arr1.push(4)
let arr3 = arr2.pop()
let arr4 = arr3.map(item => item * 2)
let arr5 = arr4.filter(item => item < 5)

console.log(arr5) // List [ 2, 4 ]

let arr6 = arr5.update(1, val => val * 100)
console.log(arr6)  // List [ 2, 400 ]

let arr7 = arr6.delete(0)
console.log(arr7) // List [ 400 ]

let arr8 = arr7.push(12)
console.log(arr8) // List [ 400, 12 ]

let arr9 = arr8.last()
console.log(arr9) // 12

immutable.js comparison method

let {is, Map} = require('immutable')
let obj1 = Map({name: 'hello', age: 12})
let obj2 = Map({name: 'hello', age: 12})

console.log(Object.is(obj1, obj2)) // false
// immutable's is method compares values
console.log(is(obj1, obj2)) // true

PureComponent component modification

import React, { Component } from 'react';
import { is } from "immutable";
class PureComponent extends Component{
  shouldComponentUpdate (nextProps, nextState) {
    // Return! Delete this line
    // Get the old and new status, and compare them at the back
    let oldState = this.state || {}
    let newState = nextState ? nextState : {}
    // If the property keys are different, update the page directly
    let keys = Object.keys(newState)
    if (Object.keys(oldState).length !== keys.length) return true;
    for (var i = 0, len = keys.length; i < len; i++) {
      let key = keys[i]
      // If the property values are different, the status needs to be updated
      if(!is(newState[key], oldState[key])) return true
    }
    return false
  }

}

export default PureComponent

index.js component modification

import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import PureComponent from './PureComponent';
import _ from 'lodash'
let immutable = require('immutable')
let { Map, List, fromJS } = immutable
// The principle of PureComponent component optimization is to rewrite the shouldComponentUpdate. If the old state object ang and the new state are not the same for the play, the page will be refreshed
/**
 * 1.Every time a new object is generated, deep cloning consumes a lot of memory
 */
class Counter extends PureComponent{
  state = {
    counter: Map({ number: 0})
  }
  handleClick = event => {
    // Optimized deep copy for memory savings
    // let state = _.cloneDeep(this.state) delete this line
    let amount = this.amount.value ? Number(this.amount.value) : 0
    // Add the following status
    let newState = { ...this.state, counter: this.state.counter.update('number', val => val + amount)}

    this.setState(newState)
  }
  render () {
    console.log('render')
    return (
      <div>
        <p>{this.state.counter.get('number')}</p>
        <input ref={input => this.amount = input}/>
        <button onClick={this.handleClick}>add</button>
      </div>
    )
  }
}

ReactDOM.render(<Counter></Counter>, document.getElementById('root'));

summary

Through simple processing, it will greatly improve the performance of the solution

Posted by nashirak on Wed, 18 Dec 2019 13:10:59 -0800