Exploring Advanced React Components and Lifecycle

Sonny Recio   Source Code  Print 
20 Sep 2018
22 Sep 2018
Intermediate
173

Last time we defined the two kinds of components and its differences as well as use-cases of when are we going to use which is which. So today, let’s delve deeper into advanced concepts of what the components can do. Without further ado, let us elaborate and dissect the advanced components further.

Component Lifecycle

In my last article, I mentioned that only class-based components have “lifecycle hooks” that runs once React starts to dock the component within the DOM. So, a component lifecycle refers to a series of the process a component will undergo once React starts to insert the component from initializing down to methods that correspond with the component’s current behaviour. Let’s have a look at the image below for reference of the lifecycle methods.

Now you noticed we have 3 kinds of lifecycle flow here along with methods that triggers along with it: Mounting, Updating, Unmounting Depending on the scenario and current state a component is into, one of the lifecycle flow will be triggered until it completes the process. Let’s explain them one by one :

Mounting

This is the most common lifecycle flow that will be triggered once React renders the components. Mounting lifecycle will be triggered when a component is being instantiated and inserted within the DOM tree. You could say this is also the starting point. The following methods will be called once a component is instantiated:

 constructor()
 getDerivedStateFromProps()
 render()
 componentDidMount()

Here's the code snippet for mounting lifecycle :

import React, { Component } from 'react';
// import axios from 'axios';

import AnotherComponent from './AnotherComponent';
import Todo from './Todo';
import ParentComponent from './ParentComponent';

export default class App extends Component {

 constructor(props) {
 super(props);
 this.state = {
 val: "Click me to change me"
 }

 console.log("constructor executed");
 }

 componentDidMount = () => {
 console.log("componentDidMount() executed");
 }
 
 static getDerivedStateFromProps(nextProps, prevState) {
 console.log("getDerivedStateFromProps() executed");
 }
 
 render() {
 console.log("component rendered..");
 
 return (
 <div>
 <h2>
 <AnotherComponent />
 <Todo />
 </h2>
 </div>
 )
 }
}

When we put console.log in each method, we see an interesting and similar pattern

Updating

This lifecycle will be triggered once a change in props or state is initiated. Then, the component will be re-rendered while running the lifecycle.The following methods will be called once a component is updated

 getDerivedStateFromProps()
 shouldComponentUpdate()
 render()
 getSnapshotBeforeUpdate()
 componentDidUpdate()

Here's the code for updating lifecycle :

import React, { Component } from 'react';
// import axios from 'axios';

import AnotherComponent from './AnotherComponent';
import Todo from './Todo';
import ParentComponent from './ParentComponent';

export default class App extends Component {

 constructor(props) {
 super(props);
 this.state = {
 val: "Click me to change me"
 }

 console.log("constructor executed");
 }

 // componentDidMount = () => {
 // // throw "error";
 // console.log("componentDidMount() executed");
 // }
 
 // componentWillUnmount = () => {
 // console.log("component unmounted...");
 // }

 static getDerivedStateFromProps(nextProps, prevState) {
 console.log("getDerivedStateFromProps() executed", nextProps, prevState);

 return null;
 }

 getSnapshotBeforeUpdate = (prevProps, prevState) => {
 console.log("getSnapshotBeforeUpdate() executed", prevProps, prevState);
 }

 componentDidUpdate = (prevProps, prevState) => {
 console.log("componentDidUpdate() executed", prevProps, prevState);
 }
 
 render() {
 console.log("component rendered..");
 
 return (
 <div>
 <h2>
 <AnotherComponent />
 <Todo />
 </h2>
 </div>
 )
 }
}

When we put console.log in each method, we see an interesting and similar pattern :

Unmounting

This lifecycle will be triggered once a component is removed from DOM tree.The following methods will be called once a component is unmounted:

 componentWillUnmount()

Here's the code for unmountinglifecycle:

import React, { Component } from 'react';
// import axios from 'axios';

import AnotherComponent from './AnotherComponent';
import Todo from './Todo';
import ParentComponent from './ParentComponent';

export default class App extends Component {

 constructor(props) {
 super(props);
 this.state = {
 val: "Click me to change me"
 }

 console.log("constructor executed");
 }

 componentDidMount = () => {
 throw "error";
 }
 
 componentWillUnmount = () => {
 console.log("component unmounted...");
 }

 static getDerivedStateFromProps(nextProps, prevState) {
 console.log("getDerivedStateFromProps() executed", nextProps, prevState);

 return null;
 }

 // getSnapshotBeforeUpdate = (prevProps, prevState) => {
 // console.log("getSnapshotBeforeUpdate() executed", prevProps, prevState);
 // }

 // componentDidUpdate = (prevProps, prevState) => {
 // console.log("componentDidUpdate() executed", prevProps, prevState);
 // }
 
 render() {
 console.log("component rendered..");
 
 return (
 <div>
 <h2>
 This component will not be rendered because the error was thrown during mount...
 </h2>
 </div>
 )
 }
}

When we put console.log in each method, we see an interesting and similar pattern

To trigger unmount lifecycle, I have to throw a dummy exception the moment the component was mounted. So this is usually triggered when something happens with your component logic or some exceptions that were thrown by other factors. It’s probably best to put your loggers in this area if you have one.

Nested Components

You can nest components within React so easily just as nesting HTML DOM. Let’s see some examples

You can see that in App component, we nested another two components: another component, Todo.

We can easily nest it in JSX by just how you nest HTML elements. You can nest and put more components for as long as you like regardless if it’s a functional or class-based component. React can effortlessly nest those components for all you like.

Components Inheritance

Just as how we inherit parent classes from one to another child in Object-Oriented Programming, we could also do the same for components in React. Take note that Reactjs team doesn’t recommend component inheritance as what the official docs say :

"At Facebook, we use React in thousands of components, and we haven’t found any use cases where we would recommend creating component inheritance hierarchies. Props and composition give you all the flexibility you need to customize a component’s look and behaviour in an explicit and safe way. Remember that components may accept arbitrary props, including primitive values, React elements, or functions."

Depending on the use-case of your application, you might be able to safely get away from using component inheritance. If you still insist in using it, let’s try to do some sample with this code snippet:

import React, { Component } from 'react';
// import axios from 'axios';

import AnotherComponent from './AnotherComponent';
import Todo from './Todo';
import ParentComponent from './ParentComponent';

export default class App extends ParentComponent {

 constructor(props) {
 super(props);
 this.state = {
 val: "Click me to change me"
 }

 console.log("constructor executed");
 }

 componentDidMount = () => {
 throw "error";
 }
 
 componentWillUnmount = () => {
 console.log("component unmounted...");
 }

 static getDerivedStateFromProps(nextProps, prevState) {
 console.log("getDerivedStateFromProps() executed", nextProps, prevState);

 return null;
 }

 getSnapshotBeforeUpdate = (prevProps, prevState) => {
 console.log("getSnapshotBeforeUpdate() executed", prevProps, prevState);
 }

 componentDidUpdate = (prevProps, prevState) => {
 console.log("componentDidUpdate() executed", prevProps, prevState);
 }
 
 render() {
 console.log("component rendered..");
 
 return (
 <div>
 <h2>
 This component will not be rendered because the error was thrown during mount...
 </h2>
 </div>
 )
 }
}

We have created a parent component that we can use to inherit into the child component. We simply have to use the parent component to “extend” the App component instead of using React.Component class.

When looking at the lifecycle behaviour, we see an interesting pattern here in the console output :

We see here that the constructor is triggered from parent component, getDerivedStateFromProps() from App, render() from App, componentDidMount() from parent component.

Now this is getting interesting and confusing at the same time. No wonder why React team almost never recommended inheriting components because this will disrupt the normal flow of lifecycle. This is effectively interrupting from the normal flow of lifecycle interchanging from the parent or the child component. They recommend using a composition model rather than inheritance

Conclusion

Now that we have learned components in-depth, it’s time for more hacking in React!

Hands-on Learning
Free Interview Books
 
+