Search code examples
javascriptreactjsimmutabilityvirtual-dom

Understanding immutability and virtual DOM in below case


After reading here about immutability, I am trying to understand how react is working. I am trying to understand with the help of following 3 components, however its not making sense.

const bla = {a: 1};
class Test extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      a: 1,
    };
    this.onClick = () => this.setState(prevState => ({
      a: prevState.a + 1
    }));
  }
  render() {
    console.log('Render default');
    return <div id="test" onClick = {
      this.onClick
    } > DOM Updating: {
      this.state.a
    } < /div>;
  }
}

class Test1 extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      a: 1,
    };
    this.onClick = () => this.setState(prevState => ({
      a: 1
    }));
  }
  render() {
    console.log('Render 1');
    return <div id="test1" onClick = {
      this.onClick
    } > DOM not updating: {
      this.state.a
    } < /div>;
  }
}

class Test2 extends React.Component {
  constructor(props) {
    super(props);
    this.state = bla;
    this.onClick = () => {
      const mutating = this.state;
      mutating.a = this.state.a + 1;
      this.setState(mutating);
    };
  }
  render() {
    console.log('Render 2');
    return <div id="test2" onClick = {
      this.onClick
    } > DOM updating with mutation: {
      this.state.a
    } < /div>;
  }
}
ReactDOM.render( < div > < Test / > < Test1 / > < Test2 / > < /div>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

If I inspect div id="test1" it is updating in Chrome dev tool on each click. Want to understand below points:

  1. During on click If I inspect div id="text1" why this is not reflecting in dev tool? Here I am assigning new object in state on each click ({a: 1}). As the link above says

In fact, React.js does not need to have knowledge about what exactly changed. All it needs to know is whether the state changed at all or not.

  1. In Test2 component I am mutating the state however, why dom getting updated on click?
  2. In case of updating nested state since we are updating parent state, does this mean React will think that all others children have changed their values and will re-render all of them even when they aren't changed?

Solution

  • 1.) In the console output, you simply see Render 1, because you invoke setState with the static value 1. But in fact, each time you invoke setState with the click, React will set a new (in the sence of new object reference) state for the component, and a new render cycle is triggered.

    setState() will always lead to a re-render unless shouldComponentUpdate() returns false. Link

    2.) See answer 1. In addition, you have a DOM change, because state changes are merged.

    When you call setState(), React merges the object you provide into the current state.

    In your Test2 click handler code, mutating will be merged into the current state. It is the same object, but important is, that all its properties (here a property) will be spread into the state, resulting in a new state object.

    const mutating = this.state;
    mutating.a = this.state.a + 1;
    this.setState(mutating);
    

    3.) Let's say you have a Parent component containing all your child components. If you change props or state of Parent, all children will be rerendered by React, unless their shouldComponentUpdate hook returns false or they are PureComponents, whose props or state did not change.

    setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state. Link

    Rerender means, that for each child, React compares the previous and the current version in a Reconciliation step. It only leads to a DOM update, if the child has really changed, otherwise nothing happens. See here for a nice visualization.