Intro to React Hooks- Part 8

Michael Rosenberg
5 min readJun 3, 2021

In the last part of this series, we went over an example of using the effect Hook, useEffect. One important point to take away from that last post is that the useEffect Hook is called after every single render.

However, in some cases, applying an effect after every render might create some issues such as performance problems. It would be nice if we had a way to conditionally run an effect from a functional component. Lucky for us, there is in fact a way to do this, and that’s what we’ll cover here in Part 8.

Class Component Example

First, let’s take a look at the Class Component implementation so that we can then compare to the Functional Component implementation using the useEffect Hook. See below code which is a continuation from the example we covered in Part 7:

// ClassCounter.jsimport React, { Component } from 'react';class ClassCounter extends Component {
constructor(props) {
super(props)
this.state = {
count: 0,
name: ''
}
}

componentDidMount() {
document.title = `Clicked ${this.state.count} times`
}
componentDidUpdate(prevProps, prevState) {
console.log('Updating document title')
document.title = `Clicked ${this.state.count} times`
}
render() {
return (
<div>
<input
type='text'
value={this.state.name}
onChange={e => {
this.setState({ name: e.target.value })
}}
/>
<button onClick={() => this.setState({ count: this.state.count
+ 1 })}>
Click {this.state.count} times
</button>
</div>
)
}
}
export default ClassCounter

As a refresher, here we have a class component, with a state variable count initialized to 0. Then, in the render function, we have a button with an onClick that increases the count value by 1. When the value increases, the state changes which causes the component to re-render, and then componentDidUpdate will execute, setting the document title to the updated counter value.

Adding on to the original code from Part 7, we then add a text input to this class component, which will accept a name from the user. We have a state variable, name, initialized to an empty string. Then, in the JSX, we have an input element with a value equal to this.state.name. Then, to capture the input element value, we add an onChange handler which updates the state variable name based on the event target value, or the text in the input field. Lastly, in componentDidUpdate, we add a log statement.

If you hop back to your browser and open the console, and click on the button, you’ll see the log statement appear in the console and the document title updates as well. But if you start typing in the input field, you’ll see that we still get the log statement and the count value is still 1. We’re basically setting the document title to the same string multiple times which is unnecessary.

We can optimize this by comparing the count value before and after the update, and if the count value changes, we can then conditionally update the title. To achieve this, we’ll include the parameters for componentDidUpdate, prevProps and prevState.

Let’s take a look at the following additions in bold:

componentDidUpdate(prevProps, prevState) {
if(prevState.count !== this.state.count) {
console.log('Updating document title')
document.title = `Clicked ${this.state.count} times`
}

}

Within the body, we check if previous state count is different from current state count and only then update the document title, using a simple if statement.

If you now go back to the browser and click on the button, the title gets updated and you’ll see the log statement, and if you start typing in the input field the document title is no longer updated. Here, we are conditionally updating the title only when the appropriate variable changes. So only when the count value changes.

Now that we know what we’re trying to implement, let’s see how using the effect Hook in a functional component allows us to do this even more easily.

Functional Component with useEffect Example

Continuing with the code from Part 7:

// HookCounter.jsimport React, { useState, useEffect } from 'react';function HookCounter() {
const [count, setCount] = useState(0)
const [name, setName] = useState('')

useEffect(() => {
console.log('useEffect- Updating document title')
document.title = `Clicked ${count} times`
})

return (
<div>
<input
type='text'
value={name}
onChange={e =>
setName(e.target.value)
}
/>
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
<div>
)
}
export default HookCounter

Let’s go over the additions in bold. First, we create a new state variable for the name input element, setting it to an empty string. Then, we add the input element, with a value set to the name state variable and an onChange that calls setName, passing in the value of the event target. Lastly, in useEffect, we add a log statement.

If you now go back to the browser and click the button, you’ll see that useEffect is called to update the title. But when you begin typing in the input field, we’re still updating the document title as the log message is showing up multiple times. This is still not quite what we want, as there’s no reason to update the title if it’s not changing between renders.

So, how do we tell React to conditionally run useEffect only when the count value changes? In the class component example, we added an if statement to check and compare the previous state with the current state, and only update the document title if there was a difference. However, since this is such a common pattern, React actually built this into the useEffect Hook. For conditionally executing an effect, we have to pass in a second parameter, which is an array.

See below additions in bold:

useEffect(() => {
console.log('useEffect- Updating document title')
document.title = `Clicked ${count} times`
}, [count])

Within this array, we need to specify either props or state that we need to watch for. Only if the props and/or state specified in this array change does the effect get executed.

For our example, we want the effect to be executed only when the count value changes. So within the array, we include count. If you now go back to the browser and click on the button, the effect is run, and now when you start typing in the input field the effect is no longer being executed after each render!

Summary

The main point to take away from this example is that in order to conditionally run an effect, you need to specify a second parameter to useEffect. That second parameter is the array of values that the effect depends on. And if the values don’t change between renders, then the effect is not run.

Thanks as always for reading and see you next time!

--

--

Michael Rosenberg

Software Engineer/Full Stack Web Developer | Ruby, JavaScript, React, Redux