React - When React trigger re-render?

Hi, I just start learning react, and I am in the ‘state’ part now,
I just want to confirm on what I understand,

The React will trigger the re-render(replacing the Virtual DOM to the Real DOM) when the Virtual DOM has changed.
In more details, State is encapsulated, we cannot able to modify it unless we changes the props in the state.

The following code are from the https://daveceddia.com/why-not-modify-react-state-directly/

class App extends Component {
  // Initialize items to an empty array
  state = {
    items: [] 
  };

  // Initialize a counter that will increment
  // for each item ID
  nextItemId = 0;

  makeItem() {
    // Create a new ID and use
    // a random number as the value
    return {
      id: this.nextItemId++,
      value: Math.random()
    };
  }

  // The Right Way:
  // copy the existing items and add a new one
  addItemImmutably = () => {
    this.setState({
      items: [...this.state.items, this.makeItem()]
    });
  };

  // The Wrong Way:
  // mutate items and set it back
  addItemMutably = () => {
    this.state.items.push(this.makeItem());
    this.setState({ items: this.state.items });
  };

  render() {
    return (
      <div>
        <button onClick={this.addItemImmutably}>
          Add item immutably (good)
        </button>
        <button onClick={this.addItemMutably}>Add item mutably (bad)</button>
        <ItemList items={this.state.items} />
      </div>
    );
  }
}

The Code above will have two buttons to demonstrate why modifying the state object won’t trigger the re-render.
When pressing AddItemImmutably button, it will work as expected that adding new element into the array.
When pressing the AddItemMutably button, it won’t work; it doesn’t add any elements

From what I understand, the items:[] inside the state is called props

state = {
  items:[]
};

About the Right Way, because the code changing the state props (items:[]), so it works.

However, in the Wrong way,

this.state.items.push(this.makeItem());
this.setState({ items: this.state.items });

Since the state is already encapsulated and using the push method will get prevented by the React?
So actually the first line won’t do anything. Since the items didn’t get any changes and the second line will just assigning the same items, that’s why it won’t add new elements into the array.

One more example,

class App extends React.Component {
    constructor() {
        super()
        this.state = {
            count: 0
        }
    }
    
    handleClick() {
        this.state.count++
    }
    
    render() {
        return (
            <div>
                <h1>{this.state.count}</h1>
                <button onClick={this.handleClick}>Change!</button>
            </div>
        )
    }
}

The above code have a button that pressing it will increase the counter count.
Same situation, because we modify the count directly, so it get prevent by the react and it won’t work.

Sorry for long long paragraphs and Thanks

Correct. You can’t modify the state directly because they are immutable. That’s why setState method is introduced, so that you can give it a copy of an object so that the previous state can compare with the new state for Virtual DOM purposes.

Thank
one more question,
how react handle/process any code that try to mutate the state but not inside the setState()?
For instances,

handleClick() {
        this.state.count++ //<- this line
    }

or

  addItemMutably = () => {
    this.state.items.push(this.makeItem()); //<- this line
    this.setState({ items: this.state.items });
  };

throw an error?

It is not that you can not mutate state, it is that React expects all state changes to occur via the setState method. It will not throw an error if you write this.state.count++, because this is valid JavaScript. It just does not work as expected with React.

Thank,
Could you give more details about what you mean “it does not work as expected”?
Here’s the full version of the code

class App extends React.Component {
    constructor() {
        super()
        this.state = {
            count: 0
        }
        this.handleClick = this.handleClick.bind(this)
    }
    
    handleClick() {
        this.state.count++
       console.log(this.state.count) //the count does increased
                                                    //but the screen display doesn't change
    }
    
    render() {
        return (
            <div>
                <h1>{this.state.count}</h1>
                <button onClick={this.handleClick}>Change!</button>
            </div>
        )
    }
}

When I click the change button, the display doesn’t change. But when I console.log the this.state.count, I can see the value has changed. Would you explain the reason?

Thank

Inside any given method like handleClick, this.state is just a normal JavaScript object. so when you increment the property count and console.log it, you will see the incremented value displayed in the console. However, React expects state changes to occur via setState, so it is not aware of what happens . It is not until you use setState, that React “sees” the change in state.

    handleClick() {
      this.setState(prevState => (
        { count: prevState.count + 1 }
      ));
    }

Note: You will notice I have used a slightly different way to use setState. This is known as functional setState and is highly recommended to use to guarantee state always as the latest changes when basing the next value of state on a previous value of state.

I have created an example of using both ways for a particular use case and you will see the functional setState works as intended, where the regular setState does not. I believe this is a good example of showing how the asynchronous nature of setState can be a “gotcha” without using the functional version of setState.

Thank a lot, I think I got it.
Just wanna summarize it:
React triggers re-render (replacing Virtual DOM to Real DOM) when it tracks there is state changes in Virtual DOM.
In React, State is encapsulated. In order to mutate/change the state, we have to put changes in the setState method. Any mutations to the state outside of setState cannot be track down by React but can be track down by JS since it is JS code. Let me know if I am wrong.

And really really appreciate for the replies

That summary is pretty good.