Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

React Lifecycle Methods - Nested Components

4.86/5 (3 votes)
9 Jun 2016CPOL5 min read 18.8K   84  
This note talks about the React life-cycle methods with the nested components that I felt important enough to keep a note.

Introduction

This note talks about the React life-cycle methods with the nested components that I felt important enough to keep a note.

Background

In an earlier note, I talked about the order of the execution of the life-cycle methods in a non-nested single React component. In this note, I will give an example to show the order of the execution of the life-cycle methods with nested React components.

Image 1

The attached is a Java Maven project. The example is implemented in the "nested-component-life-cycle.html" file. If you are interested in exploring the Maven project, you can take a look at this link and this link. If you do not use Java, it is OK because the attached is a static "html" file that you can load from any web servers. When the "nested-component-life-cycle.html" loads, it will be displayed in the browser as the following:

Image 2

  • The React components are nested to 3 layers;
  • Each checkbox is used by the "shouldComponentUpdate()" method of a corresponding component. If checked, the method will return true, otherwise false.

Although this note is a simple subject, I will recommend to take a look at the simpler non-nested case first, which will make your reading of this note easier. If you are new to React, I will strongly recommend you to take a look at it, so you can familiar yourself with the React syntaxes and the context of this note. I have tried my best to make this note simple, but I have to admit that this is a relatively advanced topic.

The "nested-component-life-cycle.html" File

The Nested Structure

In order to implement the nested components easier, I kept the nested structure in a Json object.

JavaScript
var tree = {
    label: 'Root',
    bgColor: 'lightpink',
    level: 0,
    children: [
        {
            label: 'A',
            bgColor: 'lightblue',
            level: 1,
            children: [
                {
                    label: 'A-1',
                    bgColor: 'lightyellow',
                    level: 2
                },
                {
                    label: 'A-2',
                    bgColor: 'lightyellow',
                    level: 2
                }
            ]
        },
        {
            label: 'B',
            bgColor: 'lightblue',
            level: 1,
            children: [
                {
                    label: 'B-1',
                    bgColor: 'lightyellow',
                    level: 2
                },
                {
                    label: 'B-2',
                    bgColor: 'lightyellow',
                    level: 2
                }
            ]
        }
    ]
};
  • The Json object represents a tree structure starting from the "Root" to the leaves "A-1", "A-2", "B-1", and "B-2";
  • I will traverse the Json tree to generate the nested React components. 

The Checkboxes

By traversing the Json tree, the following code creates a React class that can be used to render all the checkboxes to the browser. The HTML attribute "id" of each checkbox will be "ck" + the label of the Json node.

JavaScript
var chkClass = function(node) {
    return React.createClass({
        render: function() {
            var renderNestedNode = function(node) {
                var label = node.label;
                var children = node.children;
                
                var o = {type: 'checkbox',
                    id: 'ck' + label,
                    defaultChecked: true
                };
                var current = React.createElement('label', null,
                        React.createElement('input', o), label);
                
                if (!children || children.length == 0) {
                    return React.createElement('ul', null,
                            React.createElement('li', null, current));
                }
                
                var lis = [];
                lis.push(React.createElement('li', {key: 0}, current));
                for (var i = 0; i < children.length; i++) {
                    lis.push(React.createElement('li', {key: i + 1},
                            renderNestedNode(children[i])));
                }
                
                return React.createElement('ul', null, lis);
            };
            
            return renderNestedNode(node);
        }
    });
}(tree);

The Nested React Classes for Test

By traversing the Json tree, the following code creates a Nested React class. Each component in the nested class has its own implementation of the life-cycle methods. We will use this nested class to test the execution order of the life-cycle methods.

JavaScript
var nestedClass = function(node) {
    var getReactClass = function(node, children) {
        var label = node.label;
        var bgColor = node.bgColor;
        var level = node.level;
        
        var indent = '';
        for (var i = 0; i < level; i++) {
            indent = indent + '    ';
        }
        var log = function(method) {
            console.log(indent + label + ': ' + method);
        };
        
        return React.createClass({
            getInitialState: function() {
                log('getInitialState');
                return {updateid: 0}; 
            },
            componentWillMount: function() {
                log('componentWillMount');
            },
            componentDidMount: function() {
                log('componentDidMount'); 
            },
            shouldComponentUpdate: function(nextProp, nextState) {
                var id = 'ck' + label;
                var ckd = document.getElementById(id).checked;
                if (ckd) {
                    log('shouldComponentUpdate - true');
                    return true;
                }
                
                log('shouldComponentUpdate - false');
                return false;
            },
            componentWillUpdate: function() {
                log('componentWillUpdate');
            },
            componentDidUpdate: function() { 
                log('componentDidUpdate');
            },
            componentWillUnmount: function() {
                log('componentWillUnmount'); 
            },
            update: function() {
                this.setState({updateid: this.state.updateid + 1});
            },
            unmount: function() {
                var node = ReactDOM.findDOMNode(this);
                ReactDOM.unmountComponentAtNode(node.parentNode);
            },
            render: function() {
                var updateBtn = React.createElement('button',
                                {onClick: this.update}, 'Update');
                var unmountBtn = React.createElement('button',
                                {onClick: this.unmount}, 'Unmount');
                
                var buttons = React.createElement('div',
                        null, updateBtn, unmountBtn);
                
                var updateLbl = label
                    + ' update - ' + this.state.updateid;
                
                var cElements = [];
                if (children) {
                    for (var i = 0; i < children.length; i++) {
                        var c = React.createElement(children[i]);
                        
                        cElements.push(React.createElement('div', {key: i}, c));
                    }
                }
                
                return React.createElement('div',
                        {className: 'border', style: {backgroundColor: bgColor}},
                            updateLbl, buttons, cElements);
            }
        });
    };
    
    var getNestedClass = function(node) {
        var children = node.children;
        if (!children || children.length == 0) {
            return getReactClass(node);
        }
        
        var classes = [];
        for (var i = 0; i < children.length; i++) {
            classes.push(getNestedClass(children[i]));
        }
        
        return getReactClass(node, classes);
    };
    
    return getNestedClass(node);
    
}(tree);

Mount to the DOM

The following code mounts the React classes to the DOM in the "window.onload" event when the page is loaded in the browser.

HTML
<div id="checks" class="border"></div>
<div id="content"></div>
JavaScript
window.onload = function() {
    ReactDOM.render(React.createElement(chkClass),
            document.getElementById('checks'));
    
    ReactDOM.render(React.createElement(nestedClass),
            document.getElementById('content'));
};

The Execution Order of the React Life-cycle Methods

The Mount Process

In a Chrome browser, you can launch the developer tool by "CTL-SH*T-J". If you load the "nested-component-life-cycle.html" into the browser and look at the "console" tab in in the developer tool, you can see the following logs:

Image 3

  • During the mounting process, the "getInitialState()" and "componentWillMount()" methods for each component are called. The order in which the methods are called is from the "Root" to the leaves in the Depth-first fashion;
  • After the "getInitialState()" and "componentWillMount()" methods for all the components are called, the "componentDidMount()" method for each component is called. The order in which the "componentDidMount()" is called is from the leaves to the "Root";
  • The "componentDidMount()" method in the "Root" component is called the last. At this time, the HTML content is fully rendered in the browser.

The Update Process

If we click on any of the "Update" buttons, we can check out the life-cycle methods related to the update process. The following is the log when we click on the "Update" button in the "A-2" component:

Image 4

The log shows that only the "A-2" component is updated. Updating a child component will not trigger an update on the parent component. If we now click on the "Update" button in the "Root" component, we can see the following log:

Image 5

  • If we trigger an update on the parent component, all the child components will be updated;
  • The "shouldComponentUpdate()" and "componentWillMount()" methods on each component will be called in the Depth-first fashion;
  • After the "shouldComponentUpdate()" and "componentWillMount()" methods are called for all the components, the "componentDidUpdate()" is called is from the leaves to the "Root";
  • The "componentDidUpdate()" in the "Root" component is called the last. At this time, the HTML content is fully updated in the browser.

If we uncheck the checkbox related to the component "A" and click on the "Update" button in the "Root" component, we can see the following log:

Image 6

The "shouldComponentUpdate()" method in the "A" component returned false, which stopped the update of the "A" component. It also stopped the update of any of the child components in "A".

The Unmount Process

If we click on any of the "Unmount" buttons, we start an unmount process of the elements. But if we click on an "Unmount" button that is not the "Root" element, we will get an error message, which tells us that we cannot un-mount a child element.

Image 7

If we click on the "Unmount" button in the "Root" element, we can see the following log:

Image 8

  • The "componentWillUnmount()" method for each element is called in the Depth-first fashion;
  • When the unmount process completes, all the HTML content rendered by React is cleared from the DOM.

Points of Interest

  • This note talked about the React life-cycle methods with the nested components;
  • If you create your web pages using React alone, this subject may not be important to you. But if want to use third party or your own Javascript libraries to update the DOM, the life-cycle methods and their order of execution will play a significant role;
  • I hope you like my postings and I hope this note can help you one way or the other.

History

First Revision - 6/9/2016.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)