How to use Hooks in React

Dave Wisecarver
4 min readFeb 18, 2021

React Hooks were introduced in React 16.8 in February of 2019. There were several problems that hooks set out to resolve. For beginners, deciding whether a component should be a class or functional component can be confusing. The rules of classes can also lead to some difficulties for those learning React, since Javascript has uncommon rules for when to use this and bind. The React team developed hooks as a way to allow for state management and side effects such as the lifecycle methods componentDidMount() and componentDidUpdate() in functional components. This helps developers avoid having to refactor a functional component into a class component.

Some components might seem too simple to need to be a full class component, yet if one state variable is needed it would have needed to be refactored into a class component. This component example adds likes up in a counter using a class:

import React, { Component } from 'react';class AddLike extends Component {
constructor(props) {
super(props)
this.state = {likes: 0}
}
render() {
return (
<div>
<button onClick={() =>
this.setState({
likes: this.state.likes + 1
})}>Add Like</button>
</div>
)
}
}
export default AddLike

With React hooks, this can be rewritten as a function component like this:

import React, { useState } from 'react'function AddLikes() {
const [likes, setLikes] = useState(0)

return (
<div>
<button onClick={() => setLikes(likes + 1)}>
Add Like
</button>
</div>
)
}

The useState() function can be imported from ‘react’ and is the way to establish state in a function component. Calling useState() declares a state variable, here the variable is likes, as the first element of an array. The second element of the array will be the function that can be used to change the state variable. The only argument that useState() takes is what the initial value of that state variable should be In this case likes is set to 0. The setLikes() function can then be called anywhere that you want to change state. Instead of needing to call a function that will call this.setState{ likes: this.state.likes + 1}, you can simply set the argument of setLikes() to what the new value of likes should be.

Another common issue that React users were finding, was that lifecycle methods would often contain logic that should be used in some cases but not in others. A componentDidUpdate() might be used to after a render for a side-effect like fetching data from an api under some conditions, and setting up event listeners in others. The lifecycle methods were how these side-effects were managed, though they could get quickly complicated. For this, the React hook useEffect() was created. Calling useEffect() will allow users to separate a component into functions that can handle each of the side-effects like fetching and adding event listeners.

import React, { useState, useEffect } from 'react'function AddLikes() {
const [likes, setLikes] = useState(0)
const handleKeyDown = (event) => {
console.log(event.keyCode 'was pressed')
}

useEffect(() => {
window.addEventListener('keydown', handleKeyDown)

return () => {
window.removeEventListener('keydown', handleKeyDown)
}
}, [])

return (
<div>
<button onClick={() => setLikes(likes + 1)}>
Add Like
</button>
</div>
)
}

In this example, the event listener for ‘keydown’ was added through calling useEffect(). Previously this might have needed to be added on componentDidMount() or componentDidUpdate() and also set up with a removal on componentDidUnmount(). With useEffect() it can be all set in one place. Setting an optional return value for useEffect() is how to then remove the event listener as it would have in componentDidUnmount(). In this example, useEffect() will only run once when the component loads, because the second array argument passed to it is empty. A state variable could be passed in here and and useEffect() will run every time that state variable changes. This can improve performance as now, only the necessary action will be taken when this state change occurs, where previously, all the unrelated actions in componentDidUpdate() would have to be passed over. The following example will run a fetch request only when the likes state variable changes.

import React, { useState, useEffect } from 'react'function AddLikes() {
const [likes, setLikes] = useState(0)
useEffect(() => {//fetch request to post new likes}, [likes]) return (
<div>
<button onClick={() => setLikes(likes + 1)}>
Add Like
</button>
</div>
)
}

There are two main rules for using hooks in React. The first is that they can only be called at the top level of a component. They cannot be used inside of a loop, a conditional statement, or inside of a nested function. This is because the Hook needs to be called in the same sequence within the component in order to preserve the state of Hooks between the times that useState and useEffect are called. The other rule is that Hooks can only be imported and used within React Components. They will not work inside regular Javascript functions.

Hooks in React can be a great way for beginners to have an easier time learning React, as well as help to keep components better organized. Hooks can be very useful as an app grows in complexity, since they help keep code easier to read and more adaptable to change.

--

--

Dave Wisecarver

Software dev student and free-time game dev. Currently enrolled in Flatiron Software Engineering Program.