Quantcast
Channel: Envato Tuts+ Code
Viewing all articles
Browse latest Browse all 5163

Mastering the React Lifecycle Methods

$
0
0

Overview

React components encapsulate parts of the UI. Your full React application UI is rendered as a tree of many nested components. Depending on the flow of the applications, individual components need to perform some tasks before and after rendering as well as before and after updating.

Finally, cleanup and error handling is also important. React provides a slew of lifecycle methods that you can override and inject your own logic at the right place. In this tutorial you will learn about the lifecycle of a React component from cradle to grave, what methods are available at each stage, and when it is appropriate to override them.

Note that I use the modern ES6 classes style in this tutorial.

The PopularBar

I will use a component called the PopularBar to illustrate all the lifecycle methods and how they behave. The full source code is available on GitLab.

The popular bar contains two other components called ClickCounter. Each ClickCounter component contains a button with an emoji and displays the number of times it was clicked added to the count property it receives from its host. Here is the render() method of ClickCounter:

The PopularBar component renders two ClickCounter components with thumbs-up and thumbs-down emojis. Note that if the "show" prop is false, it renders an empty div. This will be important later when we discuss mounting and unmounting.

The first iteration of the Popular Bar

Mounting

React components exist when they are rendered by a parent component or by the root application. But before a component can be rendered, it needs to be constructed (once only) and mounted into the virtual DOM (every time it is added to the virtual DOM).

The order of events is that first the component is constructed, then the componentWillMount() method is called, the component is mounted into the virtual DOM, and then the componentDidMount()is called. That gives you plenty of opportunities to perform different types of initialization. 

Constructor

The constructor for a component will be called once per application (if you refresh the page in your browser then it's considered a new application). Here is the constructor for the PopularBar component. It doesn't really do anything except call super(), which is required, and log to the console.

The constructor for ClickCounter initializes its clickCount state to zero:

This is a perfect example of an initialization that has to be done once per application. If a ClickCounter component is mounted multiple times, it should retain its click count.

ComponentWillMount

The componentWillMount() method is called before the component is mounted, so there is no component yet. In general, there isn't too much that can be done at this stage, unless you have some special initialization that happens every time the component is mounted, but even that can wait to the componentDidMount()method. 

Here are the method implementations for PopularBar and ClickCounter:

You can call this.setState() here if you want. The props obviously are not accessible.

ComponentDidMount

Here, the component is already mounted, and you can perform any work that needs to access the component in the context of the virtual DOM. Here are the method implementations for the PopularBar and the ClickCounter. The component already exists, so its properties (props) can be accessed and displayed.

To summarize the mounting section, let's see the order of events across the PopularBar and the two ClickCounter components it contains. For your convenience, I display the emoji for each ClickCounter, so they can be distinguished. 

First, the PopularBar is constructed and its componentWillMount() method is called. Then, the constructor and componentWillMount() methods of each ClickCounter component are called, followed by the componentDidMount() calls to both ClickCounter components. Finally, the componentDidMount() method of PopularBar is called. Overall, the flow is nested where all the sub-components must be fully mounted before their containing component is fully mounted.

Updating

Once the component is mounted, it can be rendered. Every now and then, the state of the component or the props it receives from its container may change. Such changes lead to re-rendering, but the component gets a chance to be notified and even control if rendering should happen or not. 

There are four methods involved in the update process, and I'll cover them in order.

ComponentWillReceiveProps

The componentWillReceiveProps() method is called when new props are received from the container. You have access to the current props via this.props and to the next props via the nextProps parameter. Here is the componentWillReceiveProps() method of ClickCounter.

You have the opportunity here to check what props have changed and modify the state of the component if you wish. It's OK to call this.setState() here.

ShouldComponentUpdate

The shouldComponentUpdate() is a key method. It is called when either new props are received (after componentWillReceiveProps() is called) or after the state of the component is modified elsewhere. If you don't implement this method then the component will re-render every time.

But if you implement it and return 'false', then the component and its child components will not be rendered. Note that if the state of child components is modified, they will be re-rendered even if you always return 'false' from the parent's shouldComponentUpdate().

You get access here to the next props and the next state, so you have all the information needed to make a decision. The ClickCounter component displays 99+ when its count exceeds 99, so it needs to update only if the count is less than 100. Here is the code:

ComponentWillUpdate

The componentWillUpdateMethod() is called after the component shouldComponentUpdate() only if shouldComponentUpdate() returned true. At this point, you have both the next props and the next state. You can't modify the state here because it will cause shouldComponentUpdate() to be called again.

Here is the code:

ComponentDidUpdate

Finally, after the rendering, the componentDidUpdate() method is called. It's OK to call this.setState() here because the rendering for the previous state change has been completed already.

Here is the code:

Let's see the update methods in action. I will cause two types of updates. First, I'll click the thumbs up button to trigger a state change:

Note that the nextState.clickCount is 1, which triggers the update cycle. The next update will be caused by the parent passing new props. To facilitate this, I'll add a little function that triggers every 5 seconds and increments the count by 20. Here is the code in the main App component that contains the PopularBar. The change will propagate all the way down to the ClickCounter.

Here is the output. Note that the ClickCounter willReceiveProps() method has been called and the nextState.clickCount remains at zero, but nextProps.Count is now 25.

Unmounting and Error Handling

Components can be unmounted and mounted again, and there can be errors during the lifecycle of a component.

Component Will Unmount

If a component is not rendered by its container, it is unmounted from the virtual DOM and the unmounted component's componentWillUnmount() method is called. The PopularBar will not render its ClickCounter sub-components if the show prop is false. The App component renders the PopularBar and passes the show prop based on the checkbox.

Here is the App's render() method:

When the user deselects the checkbox, the PopularBar is still rendered, but it doesn't render its child components, which get unmounted. Here is the code and the output:

There is no componentDidUnmount() method because there is no component at this point.

ComponentDidCatch

The componentDidCatch() method was added recently in React 16. It is designed to help with an error during rendering that previously resulted in obscure error messages. Now, it is possible to define special error boundary components that wrap any child component that may throw errors, and the error boundary component will be rendered only if the error occurred.

Conclusion

React components have a well-defined lifecycle, and special methods let you interject your logic and take actions, control the state at a very fine-grained level, and even handle errors.

Most of the time this is not required, and you can just pass props around and implement the render() method, but it's good to know that in more specialized circumstances you will not be left staring at a black box.

Over the last couple of years, React has grown in popularity. In fact, we have a number of items in the marketplace that are available for purchase, review, implementation, and so on. If you’re looking for additional resources around React, don’t hesitate to check them out.


Viewing all articles
Browse latest Browse all 5163

Trending Articles