This. SetState is an important concept for class components,

It’s not in the function component, because the function component doesn’t have this at all

Let’s get straight to the point

Why does setState have to be immutable?

You may not understand, what is immutable value?

What is immutable value?

In fact, immutable values can be understood as pure functions of functional programming

When you modify a value, the generated value does not affect the original value

Here’s an example:

const a = 1



const b = a + 1

Copy the code

So we can understand that when WE use a to form B, a doesn’t change,

That can be interpreted as a being immutable,

After all, we also use const to define a constant a

The counter example is

let a = 1



A ++ // is the same thing as a = a+ 1

Copy the code

Obviously, a has changed, so a is not immutable

After this simple example, I think you get the idea what is immutable value

So now, why does setState have to be immutable

Why setState must use immutable values?

Not to keep you in suspense, but to get straight to the conclusion: for performance optimization

Anyone who has used react knows this

The reacat update process has a shouldComponentUpdate lifecycle function

shouldComponentUpdateHas “intercept update render” function

shouldComponentUpdateIf the return value istrueThat is to update the render, otherwise, do not update the render

If you’re confused by the above, read on

For example

import React from 'react'



export default class Page extends React.Component {

  state = {

    num: 0

  }



  handleClick = () => {

    this.setState({

      num: 0

    })

  }



  componentDidUpdate () {

    console.log('Component updated ~')

  }



  render () {

    return (

      <>

<button onClick={this.handleClick}>

      </>

    )

  }

}



Copy the code

This is a very simple piece of code. The original value of num is 0, and the value of num is also 0 when the button is clicked

Effect:


At setState, the lifecycle function componentDidUpdate triggers the update

However, let’s add a bit of code below

import React from 'react'



export default class Page extends React.Component {

  state = {

    num: 0

  }



  handleClick = () => {

    this.setState({

      num: 0

    })

  }

  

/* Start adding code */

/ * *

* React lifecycle function: Whether to update components

* @param nextProps Updated property value

* @param nextState Updated status value

* @returns {Boolean} ReturnstrueFor update, returnfalseDoes not update

* /

  shouldComponentUpdate (nextProps, nextState) {

    if (nextState.num === this.state.num) {

      // nextState

      console.log('nextState', nextState.num)

      console.log('this.state', this.state.num)

      return false// The component is not updated

    }

    return true// Component update

  }

/* End of new code */

  

  componentDidUpdate () {

    console.log('Component updated ~')

  }



  render () {

    return (

      <>

<button onClick={this.handleClick}>

      </>

    )

  }

}

Copy the code

Effect:

Now you know what “shouldComponentUpdate” means to “intercept updated renders”

However, you should also understand: “React’s parent updates, and its children update with it.”

So, if the parent component updates and the child component doesn’t want to update

We can use shouldComponentUpdate to intercept update render

Here’s an example:

Directory structure:


The parent component:

import React from 'react'

import AComp from '.. /components/AComp'



export default class Page extends React.Component {

  state = {

    num: 0,

    list: ['a'.'b'.'c']

  }



  handleClick = () => {

    this.setState({

      num: this.state.num + 1

    })

  }



  componentDidUpdate () {

    console.log('Parent component updated ~')

  }



  render () {

    return (

      <>

<button onClick={this.handleClick}>

        <p>{this.state.num}</p>

        <AComp list={this.state.list}/>

      </>

    )

  }

}



Copy the code

Child components:

import React from 'react'

import { isEqual } from 'lodash'



export default class AComp extends React.Component{



  componentDidUpdate () {

    console.log('Child component A updated ~')

  }



  shouldComponentUpdate (nextProps, nextState) {

// Introduce lodash's isEqual to compare array values for equality

    if (isEqual(nextProps.list, this.props.list)) { 

      return false// The component is not updated

    }

    return true// Component update

  }



  render () {

    const { list } = this.props

    return(

      <ul>

        { list.map((item, index) =>{

          return (

            <li key={index}>

              {item}

            </li>

          )

})}

      </ul>

    )

  }

}

Copy the code

Effect:

So, having said all this, what does it have to do with immutable values?

Let’s modify the example a little bit

So here’s where the fun comes in

The parent component:

import React from 'react'

import AComp from '.. /components/AComp'



export default class Page extends React.Component {

  state = {

    num: 0,

    list: ['a'.'b'.'c']

  }



// Modify the code

  handleClick = () => {

    this.state.num++

    this.state.list.push('d')

    this.setState({

      num: this.state.num,

      list: this.state.list

    })

  }



  componentDidUpdate () {

    console.log('Parent component updated ~')

  }



  render () {

    return (

      <>

<button onClick={this.handleClick}>

        <p>{this.state.num}</p>

        <AComp list={this.state.list}/>

      </>

    )

  }

}

Copy the code

Child components:

import React from 'react'

import { isEqual } from 'lodash'



export default class AComp extends React.Component{



  componentDidUpdate () {

    console.log('Child component A updated ~')

  }





  shouldComponentUpdate (nextProps, nextState) {

    if (isEqual(nextProps.list,this.props.list)) { 

// Introduce lodash's isEqual to compare array values for equality

      console.log('Property values of child components', isEqual(nextProps.list,this.props.list))

      return false// The component is not updated

    }

    return true// Component update

  }



  render () {

    const { list } = this.props

    return(

      <ul>

        { list.map((item, index) =>{

          return (

            <li key={index}>

              {item}

            </li>

          )

})}

      </ul>

    )

  }

}

Copy the code

Effect:


It looks like there’s nothing wrong with this, but there’s something very wrong with it

The list of state has been modified, but the child components have not been updated!

But actually, if you use shouldComponentUpdate on the parent component, it doesn’t update either

Why is that?

  this.state.list.push('d')

  this.setState({

    list: this.state.list

  })

Copy the code

Because push is going to change the array

This.state.list.push () has changed this.state.list

This. SetState is called, but it is not an error

At first glance

this.state.num++ //  this.state.num = this.state.num + 1

this.setState({

  num: this.state.num,

})

Copy the code

Also wrong, why is it updated?

Because shouldComponentUpdate defaults to true

React, the framework that many people use, does not prevent these problems

So, a hook is exposed and left to the user to use

The correct way to write the parent component is:

import React from 'react'

import AComp from '.. /components/AComp'



export default class Page extends React.Component {

  state = {

    num: 0,

    list: ['a'.'b'.'c']

  }



  handleClick = () => {

Const arr = this.state.list.slice() // Make an array copy

    arr.push('d')

    this.setState({

      num: this.state.num + 1,

      list: arr

    })

  }



  componentDidUpdate () {

    console.log('Parent component updated ~')

  }



  render () {

    return (

      <>

<button onClick={this.handleClick}>

        <p>{this.state.num}</p>

        <AComp list={this.state.list}/>

      </>

    )

  }

}

Copy the code

Child component invariant

Effect:


So here’s what you should pay attention to:

What array API changes an array, and what array API does not change an array

② Deep and shallow copies of objects

This is what front-end interviews often look at

Of course, try PureComponent if you feel like writing it every time

PureComponent makes a shallow comparison before each update

And if you want to embrace immutable values for good, check out Immutability

After all, deep copy is performance-intensive

Thank you for reading