This is the 104th original article without water. If you want to get more original articles, please search the official account and follow us.
Learn Immutable in 15 minutes

1. What is an Immutable?

Immutable Data is Data that, once created, cannot be changed. Any modification or addition or deletion of an Immutable object returns a new Immutable object. The main principle is the adoption of Persistent Data Structure (Persistent Data Structure), that is, after each modification, we will get a new version, and the old version can be preserved intact, that is, when using the old Data to create new Data, the old Data should be available and unchanged at the same time. In order to avoid the performance loss caused by Deepcopy copying all the nodes, Immutable uses Structural Sharing (Structural Sharing). For the parts that have not been modified in this operation, we can directly copy the corresponding old nodes. This is essentially Structural Sharing.

2. What are the advantages of Immutable?

2.1 Reduce complexity and avoid side effects

In JavaScript, objects are all reference types. In a scenario where data is passed by reference, there are multiple variables pointing to the same memory address, which can cause uncontrollable side effects, as shown in the code below:

Let obj1 = {name: '3'}; let obj2 = obj1; Obj2. name = 'Li si '; console.log(obj1.name); / / li si

After using Immutable:

import { Map } from 'immutable'; Let obj1 = Map({name: '3 '}); let obj2 = obj1; Obj2. Set ({name: 'bill'}); console.log(obj1.get('name')); / / zhang SAN

When we use Immutable, we reduce the complexity of JavaScript objects and make our state predictable.

2.2 Save memory

Immutable uses a structure sharing mechanism, so it reuses memory as much as possible.

import { Map } from 'immutable'; let obj1 = Map({ name: 'zcy', filter: Map({age:6}) }); let obj2 = obj1.set('name','zcygov'); console.log(obj1.get('filter') === obj2.get('filter')); Obj1 and obj2 share the same filter attribute above

2.3 Convenient Traceback

Immutable creates a new object every time it changes, and the object remains the same, so the record of changes can be saved, and the state of the application can be controlled and traceable, which is convenient for the implementation of undo and redo functions. See the following code example:

import { Map } from 'immutable'; let historyIndex = 0; let history = [Map({ name: 'zcy' })]; function operation(fn) { history = history.slice(0, historyIndex + 1); let newVersion = fn(history[historyIndex]); // append the newVersion to history.push(newVersion); // record the index, historyIndex determines if we have dropped or redone historyIndex++; } function changeHeight(height) { operation(function(data) { return data.set('height', height); }); } // let hasRedo = historyIndex! == history.length - 1; // let hasUndo = HistoryIndex! = = 0;

2.4 Functional programming

Immutable itself is a concept in functional programming, and pure functional programming is more suited to front-end development than object-oriented. Because as long as the inputs are consistent, the outputs are consistent, so you develop components that are easier to debug and assemble.

2.5 Rich APIs

Immutable implements a complete set of Persistent Data Structure and provides a lot of Data types that are easy to use. Things like Collection, List, Map, Set, Record, Seq, and a number of methods to manipulate them, including Sort, Filter, Data Grouping, Reverse, Flatten, and Creating Subsets. See the official documentation for the API

3. How to use Immutable in React

We all know that updating the React parent component will cause the child component to re-render. When we pass the component props and state only one layer, we can directly use React.PureComponent, which will automatically do a superficial comparison for us. This controls the return value of shouldComponentUpdate.

However, shallow comparisons fail when more than one level of props or state is passed, or when types Array and Object are passed. We can also use deepCopy and deepCompare in shouldComponentUpdate() to avoid unnecessary render(), but deepCopy and deepCompare are generally very performance heavy. This is where we need an Immutable.

The following example is optimized by shallow comparison:

import React, { Component } from 'react'; import ReactDOM from 'react-dom'; class Counter extends Component { state = { counter: { number: 0 } } handleClick = () => { let amount = this.amount.value ? Number(this.amount.value) : 0; this.state.counter.number = this.state.counter.number + amount; this.setState(this.state); } ShouldComponentUpdate (ShouldComponentUpdate (NeXProps, Props, Props)) nextState) { for (const key in nextState) { if (this.State[key] ! == nextState[key]) { return true; } } return false; } } render() { console.log('render'); return ( <div> <p>{this.state.number}</p> <input ref={input => this.amount = input} /> <button onClick={this.handleClick}>+</button> </div> ) } } ReactDOM.render( <Caculator />, document.getElementById('root') )

You can also do a depth comparison to determine whether the two states are equal or not and that’s a very poor performance.

ShouldComponentUpdate (nextProps, prevState) {// ShouldComponentUpdate (nextProps, prevState) {// ShouldComponentUpdate (isEqual) {return! _.isEqual(prevState, this.state); }

Immutable, on the other hand, provides a simple and efficient way to determine whether the data has changed or not. Just compare === = with is to know whether render() should be executed, and this operation costs almost nothing, so it can greatly improve performance. The modified shouldComponentUpdate should look like this:

import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { is, Map } from 'immutable'; class Caculator extends Component { state = { counter: Map({ number: 0 }) } handleClick = () => { let amount = this.amount.value ? Number(this.amount.value) : 0; let counter = this.state.counter.update('number', val => val + amount); this.setState({counter}); } shouldComponentUpdate(nextProps = {}, nextState = {}) { if (Object.keys(this.state).length ! == Object.keys(nextState).length) { return true; } // compare two objects using immutable. Is for (const key in nextState) {if (! is(this.state[key], nextState[key])) { return true; } } return false; } render() { return ( <div> <p>{this.state.counter.get('number')}</p> <input ref={input => this.amount = input} /> <button onClick={this.handleClick}>+</button> </div> ) } } ReactDOM.render( <Caculator />, document.getElementById('root') )

Immutable. Is compares the HashCode or ValueOf (for JavaScript objects) of two objects. Since Immutable uses the TRIE data structure to store it internally, the value is the same as long as the hashCode of the two objects is equal. This algorithm avoids the deep traversal comparison and has very good performance. With Immutable, as shown in the figure below, when the state of the red node changes, it will no longer render all the nodes in the tree, but only the green part of the image:

(Images from Immutable detail and React practice)

So using Immutable. Is can reduce React duplicate rendering and improve performance.

4. Immutable combined with Redux

Here is a small example of the value totals used by Immutable in conjunction with Redux:

import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types' import { createStore, applyMiddleware } from 'redux' import { Provider, connect } from 'react-redux' import immutable, { is, Map } from 'immutable'; import PureComponent from './PureComponent'; const ADD = 'ADD'; Const initState = Map({number: 0}); const initState = Map({number: 0}); function counter(state = initState, action) { switch (action.type) { case ADD: Return state. Update ('number', (value) => value + action.payload); default: return state } } const store = createStore(counter); class Caculator extends PureComponent { render() { return ( <div> <p>{this.props.number}</p> <input ref={input => this.amount = input} /> <button onClick={() => this.props.add(this.amount.value ? Number(this.amount.value) : 0)}>+</button> </div> ) } } let actions = { add(payload) { return { type: ADD, payload } } } const ConnectedCaculator = connect( state => ({ number: state.get('number') }), actions )(Caculator) ReactDOM.render( <Provider store={store}><ConnectedCaculator /></Provider>, document.getElementById('root') )

However, since the built-in CombinereEducers and InitialState in Reducer are both native Object objects, they cannot be used with Immutable native. Of course, we can achieve the compatibility effect by rewriting CombinereDers, as shown in the code below:

// Rewrite combinereDers function combinereDers (reducers) {// InitialState is initialized as an Immutable Map object return function (state = Map(), action) { let newState = Map(); for (let key in reducers) { newState = newState.set(key, reducers[key](state.get(key), action)); } return newState; } } let reducers = combineReducers({ counter }); const ConnectedCaculator = connect( state => { return ({ number: state.getIn(['counter', 'number']) }) }, actions )(Caculator)

You can also use Redux with Immutable by introducing Redux – Immutable middleware. For an application using Redux, your entire State Tree should be an Immutable. You don’t need to use normal JavaScript objects at all.

5. Points to note when using Immutable

  • Don’t mix regular JS objects with Immutable objects (don’t treat Imuutable objects as properties of JS objects, or vice versa).
  • Use the entire ReudX State tree as an Immutable object.
  • Immutable objects should be used everywhere except for display components (which are pure components and should not be used) for efficiency.
  • Use ToJS methods sparingly (this method is very performance intensive, as it traverses the data deep into JS objects).
  • Your Selector should always return an Immutable object (that is, mapStateToProps, because in React-Redux the shallow comparison is used to determine whether or not to re-redering, whereas with ToJS it would return a new object each time. That is, references are different).

6. Summary

There are many ways to optimize our React application, such as lazy loading components, using ServiceWorks to cache application state, using SSR, etc. But before considering optimization, it is best to understand how the React component works, understand the Diff algorithm, and so on. Only after understanding these concepts can we better optimize our application.

If there is anything wrong in the article, you are welcome to correct it.

Reference link:

Immutable detail and React practice

Immutable Data Structures and JavaScript

Immutable official documentation

Open source works

  • Politics gathers cloud front tabloids

Open source address www.zoo.team/openweekly/ (there is WeChat communication group on the homepage of the tabloid official website)

, recruiting

ZooTeam, a young, passionate and creative front-end team, is affiliated to the R&D Department of ZooTeam, based in the picturesque city of Hangzhou. There are more than 40 front-end partners in the team, with an average age of 27. Nearly 30% of them are full stack engineers, no problem young storm group. The members are not only “old” soldiers from Ali and netease, but also fresh graduates from Zhejiang University, University of Science and Technology of China and Hangzhou Electric University. In addition to daily business connection, the team also carries out technical exploration and practical practice in the directions of material system, engineering platform, construction platform, performance experience, cloud application, data analysis and visualization, promotes and lands a series of internal technical products, and continues to explore the new boundary of the front-end technical system.

If you want to change what you’ve been doing, you want to start doing it. If you want to change the way you’ve been told you need to think a little bit, you can’t break it. If you want to change, you have the power to do it, but you don’t need to; If you want to change what you want to do, you need a team to support it, but there is no place for you to bring people. If you want to change the established pace, it will be “5 years and 3 years experience”; If you want to change the original savvy is good, but there is always a layer of window paper fuzzy… If you believe in the power of belief, the ability of ordinary people to do extraordinary things, to meet better people. If you want to be a part of the take-off process and personally drive the growth of a front end team that has a deep business understanding, a sound technology system, technology that creates value, and influence that spills over, I think we should talk. Anytime, waiting for you to write something down and send it to [email protected]