Intro to React Hooks, useState & useEffect : What, Why, & When

Get Hooked on Hooks

React Hooks were released in February 2019 with React v16.8.0. The idea behind Hooks was to allow developers to write cleaner, less clunky code. React Hooks allow developers to use state, and other React features without having to define a class; to write code that functions like a Pure Component with state and component lifecycle methods.

Hooks are functions aptly named for their ability to ‘hook into’ React lifecycle and state within functional components (prior to Hooks this functionality was only available to class components). Hooks do not work in class components, instead allowing you to use React without classes.

Before thinking that your knowledge of React is becoming obsolete with the introduction of Hooks, know that Hooks are not here to replace your understanding of React concepts. Hooks are meant to provide a more direct API to foundational React concepts such as state, refs, lifecycle, context, and props.

“Hooks allow you to reuse stateful logic without changing your component hierarchy.” — React Documentation

The idea behind the above functionality is to provide greater ease in sharing Hooks among numerous components. Simple components can often become a convoluted mess of side effects and stateful logic. For example, as many React developers know, lifecycle methods can become a catchall for unrelated logic that needs to be accessible at certain points of the component circuition. A common example of this is the componentDidMount lifecycle method- in which it is common to fetch data, but also may be a point in the component lifecycle in which event listeners are established. This is not only antithetic to programming’s separation of concerns pilar, but can also easily introduce bugs.

Instead of having to split logic based upon lifecycle methods, Hooks allow for the division of a component into smaller functions based upon related functionality. In addition to this, Hooks have become a staple of modern React development and use of them in your application will demonstrate your proficiency within the framework.

Prior to the introduction of Hooks, one needed to create a class component to access and employ lifecycle methods. However, classes are often confusing for those learning React and can make it difficult to reuse and organize code. Even within the community of experienced React developers there is much contention between when to use a function vs class components. Hooks eliminate the issues surrounding classes, allowing for the creation of functional components that are stateful and have access to lifecycle method functionality.

Now that we have covered what Hooks are and why you should choose to incorporate them into your React applications, I will cover the useState and useEffect Hook functions and when each should be used.

The useState Hook allows for the presence of state variables within functional components. The initial state is passed to useState and a variable containing the current state value and a function to update the state value are returned. This Hook acts as the functional component’s this.state & this.setState.

Calling useState within a function component creates a single piece of state attached to that component. In class components, state is always an object while with the useState Hook state can of any type, as each piece of state holds a single value which can be an object, array, boolean, or any other data type. The most appropriate use case for useState is local component state for function components.

To incorporate useState into your React application you must (first ensure you have the appropriate React version installed) import it from the React library:

import React, { useState } from 'react';

The syntax for useState is as follows:

const [catCount, setCatCount] = useState(0);

Above, catCount is the state variable while while setCatCount is the function that is returned and used to increment the catCount variable value, the initial value of which in the above example is set to zero (aka the initial state). useState only creates one state variable, in order to create two state variables you would need to call useState again.

‘Normal’ variables in React disappear when the function exits, but state variables are preserved and accessible similar to those accessed through this.state. The function (ex. setCatCount ) returned from useState acts as this.setState.

In order to use this functionality in browser, you provide the following to the function’s return:

<button onClick={() => setCatCount(catCount + 1)}> Add Cat </button>

And to access and display the value of the state variable:

<p>You have {catCount} cats!</p>

So, all together our React function component that uses Hooks would look like this:

import React, { useState } from 'react'; //import useState

function UseStateExample() {
// Declare a new state variable, which we'll call "catCount" with initial state of zero: const [catCount, setCatCount] = useState(0); return (
<div>
<p>You have {catCount} cats!</p>
<button onClick={() => setCatCount(catCount + 1)}>
Add Cat
</button>
</div>
);
}

The useEffect React Hook acts as the componentDidMount , componentDidUpdate , and componentWillUnmount lifecycle combined for function components. Within class components, these lifecycle methods are often used in data fetching or manually changing the DOM (aka ‘side effects’ or just ‘effects’), and with the introduction of the useEffect Hook, the same functionality can be performed in function components as well.

Building off of the above example function component with the useState Hook, we will incorporate useEffect :

import React, { useState, useEffect } from 'react'; //import useState & useEffect

function UseEffectExample() {
const [catCount, setCatCount] = useState(0);useEffect(() => {
document.title = `You have ${catCount} cats!`;
});
return (
<div>
<p>You have {catCount} cats!</p>
<button onClick={() => setCatCount(catCount + 1)}>
Add Cat
</button>
</div>
);
}

Here, we are using useEffect to manipulate the DOM, changing the title element each time the catCount state variable changes. Using this Hook, the component is told to do something after render (much like componentDidMount and/or componentDidUpdate), with the passed in function (i.e. the ‘effect’) being remembered by React and called later after DOM updates are performed. The useEffect could also be used to fetch data or perform any other function that you may have used componentDidMount / componentDidUpdate for in a class component. useEffect runs after every render, the first and all those subsequent.

This example describes a situation in which there is no clean-up necessary, the functionality that is held within componentWillUnmount in a class component. However, when you are performing a component action that does need clean-up, you still depend on useEffect to get the job done in your function component. To do so, you have your useEffect return a function, which will be run when it is time to clean-up. This is a built in functionality of useEffect, every effect has the option to return to a function that cleans up after the component unmounts- the addition and removal of effects are part of the same Hook.

These Hooks help to bring state and lifecycle method functionality to function components. I encourage you (if you have not already) to try to incorporate these Hooks into your React applications! Hooks have gained traction in recent years and are likely here to stay at React improves and builds upon itself. There are a number of other React Hooks that help to extend the functionality of function components and I will continue to cover these in my next posts.

Full-stack software engineer experienced in Ruby on Rails, React, Redux, and JavaScript based programming.