Why does render not erase the state of children in Reactjs?

Viewed 68

According to the react documentation, when setState is called by the user, render is soon after called "behind the scenes". render usually returns a JSX element (though a couple other types are allowed). My question is, since render creates a new JSX element each time, and since setState called on a certain component then calls render on the same component, how is the old state of the components children retained?


I think this question is clear in and of itself, but to illustrate exactly what i mean i will give a toy example.
export class App extends Component {
        constructor (props) {
                super(props);
                this.state = {
                        foo: [null]
                };
                this.addToFoo = this.addToFoo.bind(this);
        }

        addToFoo() {
                this.setState(state => {
                        return {foo: [...state.foo, null]};
                });
        }


        render() {
                return (<div onClick={this.addToFoo}>{this.state.foo.map( el => (<Bar></Bar>))}</div>);
        }
}

with Bar defined as

export class Bar extends Component {
        constructor(props) {
                super(props);
                this.state = {color: "#111111"};
                this.changeColor = this.changeColor.bind(this);
        }
        changeColor() {
                this.setState(state => {
                        if (state.color === "#111111") 
                                return { color: "#dddddd"}
                        else 
                                return { color: "#111111"}; 
                })
        }
        render() {
                return (
                        <div style={{color: this.state.color}} onClick={this.changeColor}>
                                Howdy!
                        </div>
                );
        }
}

Here, when the child is clicked, it toggles its color between a light gray and a dark gray.

When the parent is clicked it changes its state by adding an element to foo.

render is then called on parent behind the scenes, which returns one JSX element that contains foo.length child components of type Bar. However, somehow, the previous state of the children is retained (meaning the colors of the previously included children stay the same, and only the new one is default constructed), even though the foo.length components were newly returned and did not reference the old children at all. How is this possible?


Sandbox

2 Answers

React does lots of work to check if new render returns absolutely new component, or if it is the same component. Their diffing algorithm include comparisons and some heuristics for this, but you are not required to know in details how it works, since it can change internally. In your particular case this work may result in creating absolutely new component instead of reusing previous, because you don't use keys.

More in-depth info on diffing you can read in react documentation

render() is indeed called "behind the scenes" when there is a change to the state of the parent element. But it doesn't simply instantiate all the class components again and output the result of the render() call to the browser.

Instead, React maintains a virtual DOM, a copy of the DOM visible in the browser. It compares the output of the render() call to that virtual DOM, and it is only the changes that will be output to the real DOM.

As part of carrying out this comparison, for each component it will determine if it is the same component that was there in the previous render by comparing to the virtual DOM (exactly how it does this would require a close inspection of the algorithm).

If a class component is determined to be the same component as before, it will not instantiate it again but instead call the component's render() method using the same class instance and thus the same state as before. (As this documentation explains, a class component's constructor is only called when it is mounted.)

In this way children class components can retain state.

Related