Getting started with React: Lifecycle Hooks

This blog is a part of the series ‘Getting started with React’.

In the last part, we covered the core concepts of react to get familiar with how the very basics of a react application work. A brief recap on what we learnt in the previous part:

  • The initial project structure for a react application

You can access part 1 of this series here.

We will be building a separate application to work with in this part, as it will be easier to understand things this way.

Okay, let’s begin then! In this part we will be looking at functional and class based components a little in depth.

A glimpse at what our application should look like in the end

For this, we will create a very simple alarm clock!

Generating the project

We will generate our initial project structure using create-react-app

create-react-app alarm-clock

Again, convert your functional App component into a class based component and remove the initial JSX.

As I mentioned before, we will be looking at using react hooks to create a purely function based application in a separate part.

Your src/App.js file should end up looking like this:

import React, { Component } from 'react';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<h1>Time waits for no one</h1>
</div>
);
}
}
export default App;

The render method is used to return the JSX which we have to “render” to a web page, in class based components. It is required by any class based component to have a render() method present.

This is because all class based components have a component lifecycle. Basically, stuff a component does at different points in time - such as when your component is just inserted into the DOM tree, or when an event is trigerred which updates your component, or when such an update has already occurred, or when a component is just removed from the DOM tree (unmounted) etc.

And this component lifecycle is not present in functional components!

Just think that there are certain landmarks before and after we finally see the JSX rendered to our screens as HTML, and class based components can execute operations at such landmarks.

The render( ) method is a part of this lifecycle, and is one such landmark. It is used at the point where we finally have to return JSX in a class based component.

We will build this application in a way, that these methods are used in their order of execution, and you get to know how to use these methods as intended.

We can divide the component lifecycle into three main parts:

  • Component creation

Let us have a look at these in a little depth.

Component Creation Lifecycle

The component creation lifecycle, has the following methods associated with it:

  • constructor( ), this is the normal es6/javascript constructor

constructor( )

Even though it is the default es6/js constructor method, it is still a part of the component’s lifecycle, since obviously, react is javascript. The constructor method is executed before our class based component is mounted to the DOM tree.

We can use the constructor method to initialise our state as such:

import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor() {
this.state = {
title: 'Alarm clock'
}
}
render() {
return (
<div className="App">
<h1>{this.state.title}</h1>
</div>
);
}
}
export default App;

Run your application using yarn start, and you should see the following output on your screen:

Why are we getting this error? This is because whenever you use a constructor in your react component, you have to make a call to the super( ) method. When we are creating a class based react component, we are extending it from Component:

class App extends Component {  ...}

es6 requires all subclasses implementing a constructor to make a call to super.

This is to avoid any pitfalls arising from accidentally using properties extended from the superclass instance by an instance of the subclass, before those properties are initialised in the superclass. Sounds confusing? Here is an example.

class Animal {
constructor(type){
this.type = type
}
}
class Species extends Animal {
constructor(type, name){
console.log('I am a', name);
super(type);
}
}

Suppose if javascript were to allow this, and after some time we changed the class definition for species to instead be:

class Animal {
constructor(type){
this.type = type
}
}
class Species extends Animal {
constructor(type, name){
// trying to access type before calling super console.log('I am a', name, 'and I am of type', this.type); super(type); }
}

We try to access the type from the superclass before we have passed it to an instance of the superclass. This is why javascript will throw an error as shown.

Javascript will only accept the following, so that no properties on the superclass instance are accessed before their initialisation:

class Animal {
constructor(type){
this.type = type
}
}
class Species extends Animal {
constructor(type, name){
// super is called first
super(type);
console.log('I am a', name, 'and I am of type', this.type);
}
}
const tiger = new Species('mammal', 'cat');

Hence, in the constructor of your src/App.js file, let us fix this:

constructor() {
// the call to super is mandatory
super();
this.state = {
title: 'Alarm clock'
}
}

And our page should render now as expected:

Let us look at another thing about using a constructor.

Class based components also have access to props. Let us pass the title as a prop to our App component.

In your src/index.js file, pass your title prop as shown:

...ReactDOM.render(
<App title="Alarm Clock"/>,
document.getElementById('root')
);
...

Now in our src/App.js file, let us access this prop as shown:

...class App extends Component {
constructor() {
super();
this.state = {
title: 'Alarm clock'
}
}
render() {
return (
<div className="App">
<h1>{this.props.title}</h1>
</div>
);
}
}
...

As expected, your screen will still look the same.

But wait, doesn’t this part irk you now with its redundancy? This thing here:

constructor() {
super();
this.state = {
title: 'Alarm clock'
}
}

Wouldn’t it be great if we could define our title in the constructor itself from the props? To do this, we just have to pass a props parameter to the constructor:

class App extends Component {
constructor(props) {
super();
this.state = {
title: props.title
}
}
render() {
return (
<div className="App">
<h1>{this.state.title}</h1>
</div>
);
}
}

Your application, again, will look unchanged (we are only playing around like this to know the nitty gritty details of our constructor).

Your title prop is reflected in the browser as expected:

Though an interesting thing to note is, the official react docs state the following:

The constructor for a React component is called before it is mounted. When implementing the constructor for a React.Component subclass, you should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor, which can lead to bugs.

We never passed props to super( ), right? Why didn’t we run into any errors then?

This is because “inside” react, your props are passed to the Component superclass by default after an instance of your component is initialised. Of course, you can figure this from how we can have props available even without having a constructor.

So even if you forgot to pass props to super, you will still have access to your props like we had, inside our render( ) method. React takes care that the Component superclass you’re extending from is aware of your props. However, we will still pass our props explicitly to our superclass to keep things clear. The Component class is what distributes our props after all, otherwise “App” in itself, is just an ordinary es6/js class.

Also, in the constructor block, this.props will remain undefined between your call to super( ) up until the constructor exits and react has a chance to pass props for you to the Component superclass. We will plug up all these gaps by following the guidelines as described in the official docs hence:

class App extends Component {
constructor(props) {
super(props);
this.state = {
title: this.props.title
}
}
render() {
return (
<div className="App">
<h1>{this.state.title}</h1>
</div>
);
}
}

You will generally use constructors as we did, to set up state before your component is instantiated.

Another thing which would be bugging you would be from how we defined state in the previous part of this series:

...class App extends Component {
state = {
heroes: [
{id: 1, name: 'Geralt', power: 'Witcher'},
{id: 2, name: '2B', power: 'Android'},
{id: 3, name: 'Aloy', power: 'Nora Brave'},
{id: 4, name: 'Emily Caldwin', power: 'Master Assassin'},
{id: 5, name: 'Artyom', power: 'Master Survivor'},
{id: 6, name: 'Dohvakiin', power: 'Dragonborn'}
]
};
...}

We did not use a constructor there, instead defined our state directly at the top like this. And if you do think of it in terms of conventional OOP as well, it gets you scratching your head. How did our state exactly get initialised?

This follows the proposed es6 class field syntax, though it has still not become mainstream. It definitely has the potential to, and is being widely adopted in the react community for how concise it is. You can skip the explicit constructor definition using this after all.

You can picture it as syntactic sugar for now, which behind the scenes is converted to the constructor syntax we implemented before.

To sum up, our src/App.js component looks like this currently:

...class App extends Component {
constructor(props) {
super(props);
this.state = {
title: this.props.title
}
}
render() {
return (
<div className="App">
<h1>{this.state.title}</h1>
</div>
);
}
}
...

static getDerivedStateFromProps( )

This method is used to update the internal state of a component, in response to changes to external props which are passed by a parent component.

This is however, anti pattern to how we would like to code our components. Our components should have the data they hold to be fully controlled, where data is passed and controlled completely by the parent component. Or, it should be fully uncontrolled, i.e. it manages its own internal state which the parent cannot manipulate directly.

This method is also fired for every re-render. You can imagine how that would be undesirable, moreover creating your own instance of an internal state from the state of a parent would mean that there are two sources of truth now. It must always return a state object for what the state should look like next.

This will be used in very rare cases, and you must always think of a better approach if you find yourself needing to use this method. Here is a post which explains this far, far better.

render( )

This is the method which we have seen the most of so far. This is because it is the only required lifecycle method in a class based react component.

The render( ) method is responsible for rendering the JSX we wrote, to the DOM. For this, it examines this.props and this.state. This is because these are the only two things from where a component can possibly update, and will need to be re-rendered. If a component does not have any props or any state, then it is basically just plain HTML, and will never need to be rendered again after its initial render to the DOM.

Props, for external changes and the state for internal changes. Therefore, we should keep the render( ) method pure, and free from any pieces of code which might cause changes to the state of the component!

Since such changes will update the state, and the render cycle will be kicked off again to respond to the new state. The render cycle will eventually again execute that piece of code which updated the state before, and the state update will again call the render( ) method. On and on, a seemingly infinite cycle or renders.

Another important thing to note, is that the JSX which we return should be enclosed by a parent element, or by a react fragment. Besides this, we could also return an array of react elements.

Before we move on to the next lifecycle method, let us first make the following changes to the App.js component file:

class App extends Component {  constructor(props) {
super(props);
const currentDateTime = new Date();
const currentHour = currentDateTime.getHours();
const currentMinute = currentDateTime.getMinutes();
//setting up state in the constructor
this.state = {
currentHour,
currentMinute
}

}
render() {
return (
<div className="App">
<h1>{this.props.title}</h1>
<div>
<div>
<p>
{this.state.currentHour} : {this.state.currentMinute}
</p>
</div>
</div>
</div>
);
}
}

We are now picking up the title directly from the props, and have set up our state in the constructor to get the current hours and minutes.

Your application should end up looking like this:

Let us make a separate component to display the time and hour.
This will be a functional component to which we will pass the relevant data via props.

Create a folder called components (to store components which do not create their own state, it is a convention to use a folder called components to house them in), and in this folder, create a subfolder called Time.

We will create a Time.js file and a Time.module.css file:

In your Time.js file, add the following code:

import React from 'react';const time = (props) => (
<div>
<div>
<p>
{
props.currentHour < 10
? '0' + props.currentHour
: props.currentHour
}

: {
props.currentMinute < 10
? '0' + props.currentMinute
: props.currentMinute
}

</p>
</div>
</div>
);
export default time;

Whenever the hour or minute is returned to us in a single digit, we prepend a zero to it. We will now import this functional component in App.js:

import React, { Component } from 'react';
import Time from './components/Time/Time';
import './App.css';

and add it to the render( ) method in App.js to be rendered:

class App extends Component {
constructor(props) {
super(props);
const currentDateTime = new Date();
const currentHour = currentDateTime.getHours();
const currentMinute = currentDateTime.getMinutes();
//setting up state
this.state = {
currentHour,
currentMinute
}
}
render() {
return (
<div className="App">
<h1>{this.props.title}</h1>
<Time
currentHour={this.state.currentHour}
currentMinute={this.state.currentMinute}
/>

</div>
);
}
}

But this component doesn’t do much besides show the static time, right? We need to refresh the page for it to get the current date and time. Let’s change that behaviour using our next lifecycle method.

componentDidMount( )
The componentDidMount( ) method is executed immediately after the render( ) method. This method is where we should ideally cause all side effects, i.e. making API calls to populate data, and other such asynchronous actions. This is because reaching this method would mean the render( ) method has executed successfully and we have our JSX in place to populate the data inside, which we got as a response.

We will use this method to create a setInterval function which updates the time in intervals of every passing minute. We will place this method after our constructor in the App.js file.

...constructor(props) {
super(props);
const currentDateTime = new Date();
const currentHour = currentDateTime.getHours();
const currentMinute = currentDateTime.getMinutes();
//setting up state
this.state = {
currentHour,
currentMinute
}
}
componentDidMount() {
console.log('entering componentDidMount');
setInterval(() => {
const currentDateTime = new Date();
const currentHour = currentDateTime.getHours();
const currentMinute = currentDateTime.getMinutes();
this.setState({
currentHour,
currentMinute
})

}, 60000);
}
...

You will now see the time updating after every minute.

But do you see something that might get you scratching your head here? Do you notice the setState method?

So far, we have discussed the following flow:

  • the setState method is used to change the state for a component

Wouldn’t calling setState in componentDidMount( ) result in a re-render, then call componentDidMount( ) again, re-render, call it again and so on? An infinite cycle of state updates and re-renders?

Well, you are right for thinking this way. This would be true if we were calling setState synchronously.

However, we execute it asynchronously inside the setInterval callback, and so the setState method is not invoked immediately. It is registered, but executed at a later point in time, after we exit componentDidMount.

React will only call componentDidMount only once for a component.

And so, an extra re-render is made for the state update, but componentDidMount is not called again.

I have only used this approach to show you how all of these tiny things work, otherwise always try to avoid this extra call as well!

Add a console.log( ) if you want to see how often we call our render method. You will see it is called after every minute, as we specified in our setInterval method. The componentDidMount( ) method is only called once though, as explained before.

render() {
console.log('render called');
return (
<div className="App">
<h1>{this.props.title}</h1>
<Time
currentHour={this.state.currentHour}
currentMinute={this.state.currentMinute}
/>
</div>
);

The componentDidMount( ) is the most apt method to make any asynchronous API calls, as we can be sure that our JSX has been loaded successfully for data to be populated in it. Basically any actions, which require access to DOM elements, must be performed here.

Component Update Lifecycle

An update cycle will be triggered in only two ways for a component: state changes and prop changes.

Since so far we have only worked on displaying time/data, we have not worked significantly at all with a component that updates quite often. Our component only updated after every minute, and we did not yet need any component update lifecycle methods to respond to this update.

Now we will work on taking input from the user for a time to set the alarm for, and hence create a scenario for significantly more dynamic updates and responding to them to set an alarm.

Again, we will go in the order of execution of these state update lifecycle methods:

  • static getDerivedStateFromProps( ): as we discussed before this is a niche method, it is a part of the update cycle since it is called on every render cycle, and also since it is used to set up state from props passed in by a parent and respond to prop changes in the parent as well. More about it here. We will not be looking at this in detail here.

So, let us have a look at each of these while enhancing our alarm clock. First, we will create something so that we can take input from the user.

In your src/components/Time folder, add the following to your Time.js file:

const time = (props) => (
<div>
<div>
<p>
{
props.currentHour < 10
? '0' + props.currentHour
: props.currentHour
}
: {
props.currentMinute < 10
? '0' + props.currentMinute
: props.currentMinute
}
</p>
</div>
<div>
<input
placeholder="HH"
type="number"
min="0"
max="23"
/>
<input
placeholder="MM"
type="number"
min="0"
max="60"
/>
<button>Set Alarm</button>
</div>

</div>
);

We have added two input boxes, one to set the hours for the alarm and the other to set the minutes. We will trigger a click event on clicking the button, to set this alarm.

Of course, to track this alarm, we will need to link it to some state which we can refer to. As we have seen so far, it is good to have the state and all methods directly changing the state consolidated as much as possible.

So we will set up the alarm state in App.js and distribute it via props to the Time component.

In the constructor of your App component, add the following:

constructor(props) {
super(props);
const currentDateTime = new Date();
const currentHour = currentDateTime.getHours();
const currentMinute = currentDateTime.getMinutes();
//setting up state
this.state = {
currentHour,
currentMinute,
alarmHour: 0,
alarmMinute: 0,

}
}

And pass alarmHour and alarmMinute as props to the Time component. The render method should now look like this:

render() {
console.log('render called');
return (
<div className="App">
<h1>{this.props.title}</h1>
<Time
currentHour={this.state.currentHour}
currentMinute={this.state.currentMinute}
alarmHour={this.state.alarmHour}
alarmMinute={this.state.alarmMinute}

/>
</div>
);
}

And, we will reflect these props in the input elements as well, inside the Time component. In your Time.js file, make the input elements to look like this:

<input
placeholder="HH"
type="number"
min="0"
max="23"
value={props.alarmHour}
/>
<input
placeholder="MM"
type="number"
min="0"
max="60"
value={props.alarmMinute}
/>

So now your web app should look like below, in your browser:

Of course, since we have no two way binding set up to react to the onchange event in the input boxes, nothing will happen when you try to input something into them.

Let’s change this behaviour next. Let us create a listener method to react to every keystroke in the input boxes and update the state.

Add the following two methods after your contructor block in App.js :

...setAlarmHour = (event) => {
const hour = event.target.value;
this.setState({ alarmHour: hour });
};
setAlarmMinute = (event) => {
const minute = event.target.value;
this.setState({ alarmMinute: minute });
}
...

We will now pass these methods as props to the Time component. But we will only be passing the method references!

Again in App.js,

<Time
currentHour={this.state.currentHour}
currentMinute={this.state.currentMinute}
alarmHour={this.state.alarmHour}
alarmMinute={this.state.alarmMinute}
setAlarmHour={this.setAlarmHour}
setAlarmMinute={this.setAlarmMinute}
/>

Now we will link these methods to the onChange event, in Time.js :

<input
placeholder="HH"
type="number"
min="0"
max="23"
value={props.alarmHour}
onChange={props.setAlarmHour}
/>
<input
placeholder="MM"
type="number"
min="0"
max="60"
value={props.alarmMinute}
onChange={props.setAlarmMinute}
/>

The onChange event passes the event parameter as default, whenever the event is triggered on every keystroke. You should now be able to change the values inside the input boxes, since we have set up two way binding.

Now we just have to work on saving the alarm. We will do this by attaching a method to the “Set Alarm” button, and push the alarms to an alarms array in our state.

First, let us modify our state object in App.js, by adding an alarms array to it to keep track of saved alarms:

constructor(props) {
super(props);
const currentDateTime = new Date();
const currentHour = currentDateTime.getHours();
const currentMinute = currentDateTime.getMinutes();
//setting up state
this.state = {
currentHour,
currentMinute,
alarmHour: 0,
alarmMinute: 0,
alarms: [],
}
}

Now we will create a simple method to push the current alarmHour and alarmMinute to this array, again inside App.js (again, since we should be managing our state directly only in the component which creates this state!):

saveAlarmToList = () => { 
const updatedAlarmsList = [...this.state.alarms];
updatedAlarmsList.push({
alarmHour:
this.state.alarmHour < 10
? '0' + this.state.alarmHour
: this.state.alarmHour
,
alarmMinute:
this.state.alarmMinute < 10
? '0' + this.state.alarmMinute
: this.state.alarmMinute

});
this.setState({alarms: updatedAlarmsList});
};

Great, we should now be able to save our alarms to this list. Let us also create a view to list these, and pass the method via props to the Time component.

render() {
console.log('render called');
let alarmsListView = (
<div>
<p>No Alarms Set</p>
</div>
);
if (this.state.alarms.length) {
alarmsListView = (
<div>
{this.state.alarms.map(alarm => (
<p>{ alarm.alarmHour } : { alarm.alarmMinute }</p>
))}
</div>
)
}
return (
<div className="App">
<h1>{this.props.title}</h1>
<Time
currentHour={this.state.currentHour}
currentMinute={this.state.currentMinute}
alarmHour={this.state.alarmHour}
alarmMinute={this.state.alarmMinute}
setAlarmHour={this.setAlarmHour}
setAlarmMinute={this.setAlarmMinute}
saveAlarmToList={this.saveAlarmToList}
/>
<hr />
{alarmsListView}
</div>
);
}

Now the only thing remaining is hooking up this method to the “Set Alarm” method. In your Time component:

...  <button onClick={props.saveAlarmToList}>Set Alarm</button>...

You should be able to save your alarms to the list now.

Now that we have a basic structure for our alarm clock, let us have a look at the shouldComponentUpdate( ) component update lifecycle method.

shouldComponentUpdate(nextProps, nextState)

This method is used mainly (and should be used only) to optimise performance, and will be rarely used. It receives two arguments whenever it is implemented: the incoming props (nextProps) and the incoming state (nextState). You can have a look at these and even compare them with the current this.props and this.state to decide whether you want to continue with the update cycle or not.
In case you want to, return true from this method otherwise return false. You will have to return either of these whenever you implement this lifecycle method.

On returning false, the render cycle gets cancelled (the render() method is not invoked for the component), however be very careful whenever doing this! Even the official React docs state that you should not try to cancel render cycles unless it is absolutely required, which will rarely be the case. This is because cancelling render cycles manually can lead to some nasty bugs!

The most common scenario where you might want to implement this method, is to check whether the incoming state or props differ from the existing state and props. Of course, we would like to skip unnecessary render cycles if nothing changes practically.

Do you remember that we extended our class based components from the Component class? There also exists a PureComponent class which you can extend from instead of Component, which will automatically do a shallow comparison to see whether incoming props and state differ from the existing/previous ones, and will avoid any unnecessary render( ) cycles by cancelling the update cycle whenever it finds that the props and state haven't actually changed. More about this here.

Let us try to see this lifecylce method in action now. We will check for the scenario when the user tries to add an alarm for an invalid time. Even though we specified the “min” and “max” attributes on our input elements, they do not work as intended.

First of all, let us see the structure of our nextProps and nextState arguments by logging them in the browser. Add the lifecycle method to your App.js file as follows:

shouldComponentUpdate(nextProps, nextState) {
console.log('Inside shouldComponentUpdate');
console.log(nextProps, nextState);
// return true to proceed with the update as usual
return true;
}

Keeping you devtools open in your browser, try to type something in the input boxes to set an alarm.

You can see how your previous props and state are laid out. We will use these to cancel any input updates for an invalid time entered.

To do this, we will modify out shouldComponentUpdate( ) method to now look like this:

shouldComponentUpdate(nextProps, nextState) {
if(nextState.alarmHour > 23 || nextState.alarmMinute > 59) {
return false;
}
return true;
}

Now try to enter any values for the hour or minute, which are invalid. You will see that the input box will not render the invalid values, since we cancel the next render( ) cycle by returning false.

However, let us now also see why this approach is buggy and why it is usually better to not depend on this lifecycle method a lot. I will try to enter an alarm for the time 23:88, which is of course invalid.

As expected, it seems we cannot enter the invalid ‘88’ for the minute value.

However, look at what happens when we click ‘Set Alarm’ after entering this value and then trying to set a new alarm:

What happens here is this:

  • We enter an invalid 88 in the input box, and hence the render method for that update cycle is cancelled since we return false. However, the state update did take place from the onChange event on the input! Since state updates are not cancelled when we return false!

Therefore such behaviour, where the link between the state and the rendered DOM becomes muddled, is undesirable and should be minimised as much as possible!

render( )
We have already discussed about this method, the only difference is that this time it is executed for the update lifecycle. As we saw in the previous section, the render( ) method will not be called for any DOM updates if we return false in the shouldComponentUpdate( ) lifecyle method. Again, always try to keep the render( ) method pure, free from any side effects and state update logic!

getSnapshotBeforeUpdate(prevProps, prevState)
This method is executed after the render( ) method but just before the changes are committed to the actual DOM. We can use this to save anything required from the actual DOM just before it is updated, such as the previous scroll position. It also receives the previous state of your props and local component state as arguments.

This method will again, be rarely used. It has no real use case in our application as well, so we will simply log something from this method to see when this is fired. We return whatever data we need from the previous DOM representation, or otherwise return null. The returned value is fed as an argument to the next lifeycle method : componentDidUpdate( ), we will illustrate this when we move to the section for componentDidUpdate( ) in a short while.

Add the following before your render( ) method:

getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('getSnapShotBeforeUpdate', prevProps, prevState);
return null;
}

You can now check that this method executes after render is called:

componentDidUpdate( )

This lifecycle method is executed after the render( ) method has successfully committed the updates to the actual DOM. Remember the getSnapShotBeforeUpdate( ) method executed in between this process.

This method is similar to componentDidMount( ) in the way that it is a good place for any side effects, such as making network calls or any other such async tasks in response to events which update the component, since we can be sure at this point that the DOM did update and the UI is in a consistent state.

Since this method is only fired for updates, it is not executed for the initial render (where the component is simply mounted). Yet another way it is dissimilar to componentDidMount( ) is that where componentDidMount( ) is only updated once by react even when calling setState( ), it is not so for componentDidUpdate( ). We have to ensure that we wrap any setState( ) operations with a condition inside componentDidUpdate( ), as this can lead to an infinite loop.

Also, we receive the following arguments in this method if we require to work with them: componentDidMount(prevProps, prevState, snapshot)

The previous props before the update, the previous state before the update and the snapshot in case we return anything from the previous lifecycle method: getSnapshotBeforeUpdate( )

For example, we can use these arguments to make a network call only when our props match a certain criteria. A snippet from the official react docs:

componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}

Imagine fetchData to be a method which makes a network request and sets the state as per the fetched result. Had we not wrapped this method in the condition as shown, we would have ended up in an infinite fetching operation since every time our state would be set, the update cycle would be fired again.

This is the part where we will actually set our alarm, there were many other ways to do this but I was saving up to show how we can utilise the componentDidUpdate( ) method for async tasks such as setting a timeout counter.

Add the following code before your render( ) method:

getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('getSnapShotBeforeUpdate', prevProps, prevState);
return { msg: 'Message from getSnapShotBeforeUpdate'}
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('componentDidUpdate', snapshot.msg);
if (prevState.alarms.length !== this.state.alarms.length) {
const curHour = parseInt(this.state.currentHour);
const curMinute = parseInt(this.state.currentMinute);
const lastAlarmIndex = this.state.alarms.length - 1;
const alarmHour =
parseInt(this.state.alarms[lastAlarmIndex].alarmHour);
const alarmMinute =
parseInt(this.state.alarms[lastAlarmIndex].alarmMinute);
const hourDiff = alarmHour - curHour;
const minuteDiff = alarmMinute - curMinute;
const timeoutVal = 3600000 * hourDiff + 60000 * minuteDiff setTimeout(() => {
alert('alarm!');
}, timeoutVal);
alert('alarm set');
}
}

Of course this is not the perfect logic, but it is enough to illustrate what we’re trying to do here. We wrap our alarm in an ‘if’ condition, to not go into any unnecessary operations. We calculate the difference from our current point in time to the alarm time, and set a timeout for that interval. We do this by getting the hour difference, the minute difference and then setting a timeout for that interval. The alarm might not be exact to the second, since we do not work with seconds and register our timeout probably when a few seconds have already passed. Also note that we took this opportunity to pass a message into our snapshot argument, do check the result in the browser console!

The alarm alert will be shown as per the computation, try setting an alarm for a few minutes in the future now to verify.

Setting the alarm
Alarm set for 21:09
Alarm alert, delayed a few seconds since we are not exact to the second in our logic

Great, so we have completed our makeshift alarm clock! Though there is just one more thing left to look at, what happens when a component is unmounted.

componentWillUnmount( )

This method is called when a component is unmounted from the DOM tree, when a component is ‘destroyed’.

You can use this method to perform any last minute operations, such as cleaning local storage, session storage or resetting timers and counters associated with the component.

We will create a dummy component, just to see how this method is called.

Create a new class based component in your src folder. We’ll call this component ‘Dummy’. Create a folder src/Dummy.

Add a Dummy.js file in this folder:

import React, { Component } from 'react';class Dummy extends Component {
componentWillUnmount() {
alert('Unmounting');
}
render() {
return (
<div>
<button onClick={this.props.removeComponent}>
Remove Me!
</button>
</div>
);
}
};
export default Dummy;

We will import this component in our App component, and pass to it a method via props which we will use to unmount the Dummy component (the removeComponent method as shown here).

In you App.js file, add the following import:

import React, { Component } from 'react';
import Time from './components/Time/Time';
import Dummy from './components/Dummy/Dummy';
import './App.css';
...

We will use a flag defined in the state to show and hide this dummy component. Add this to your App component’s state:

...constructor(props) {
super(props);
const currentDateTime = new Date();
const currentHour = currentDateTime.getHours();
const currentMinute = currentDateTime.getMinutes();
//setting up state
this.state = {
currentHour,
currentMinute,
alarmHour: 0,
alarmMinute: 0,
alarms: [],
showComponent: true,
}
}
...

Add a method to set this flag to false in your App component:

...removeComponent = () => {
this.setState({ showComponent: false });
};
...

And in the return block for your render( ) method:

return (
<div className="App">
<h1>{this.props.title}</h1>
<Time
currentHour={this.state.currentHour}
currentMinute={this.state.currentMinute}
alarmHour={this.state.alarmHour}
alarmMinute={this.state.alarmMinute}
setAlarmHour={this.setAlarmHour}
setAlarmMinute={this.setAlarmMinute}
saveAlarmToList={this.saveAlarmToList}
/>
<hr />
{alarmsListView}
{
this.state.showComponent?
<Dummy removeComponent={this.removeComponent}/>
: null
}

</div>
);

We display the Dummy component depending on the showComponent flag. We pass the removeComponent method as a prop to the Dummy component, to set this flag to false and hence to remove the Dummy component from the rendered DOM.

On clicking this method, we will get the alert pop up in our browser as we specified in the code for componentWillUnmount( ) lifecycle method for our Dummy component.

The Dummy component with the button
On clicking the button, the componentWillUnmount( ) method is invoked

So that was it for this part, it was already pretty lengthy! I tried to make this blog a single point of reference for most of the things related to the main lifecycle methods for a class based component. Hopefully by now, you should have a basic understanding of how components work and how these lifecycle methods can be used to manage these components in the DOM.

Try creating your own version of such an app yourself, maybe try to create a more complicated stopwatch as a personal project. Hopefully, the other parts I publish as a part of this series will be shorter since we have covered the main fundamentals.

Happy coding!

Building things, breaking things, learning things

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store