Using React’s State Hook Inside the Effect Hook Without an Endless Re-Render

Ben Aron Davis
Analytics Vidhya
Published in
4 min readMar 25, 2020
Photo by Tine Ivanič on Unsplash

React now has Hooks that we can use with functional components instead of using class components. The two main Hooks useState() and useEffect() mirror the class component’s state and lifecycle methods.

In order to explain how we run into issues using these two Hooks together, I’ll first summarize how each of these Hooks works.

useState()

When we call useState(), it creates both the state variable as well as a function to reassign a value to that specific state variable. Whatever we pass as an argument to useState(), becomes the state variable’s initial value. Here’s what it looks like if we create a state variable called stories that we initially set to an empty array:

In our case, setStories is equivalent to this.setState only easier to use. Whatever we pass to the setStories function becomes the value for the variable stories.

useEffect()

useEffect() takes a function as an argument, and it runs that function whenever the component renders. It’s essentially a non-specific lifecycle method. If we want the console to log ‘RENDER’ every time the component renders, our useEffect() would look like this:

Here’s where we might get into trouble. If our component needs to fetch, and save something to the state, we might do that fetch in componentDidMount() in a class component. This leads us to try our fetch inside useEffect() if we’re using functional components with Hooks. Fetching inside useEffect() isn’t a problem on its own, but we encounter a problem if we try to use the data from our fetch to set the state.

This Hook breaks our program with an infinite loop

That’s when we find ourselves stuck in an infinite loop of rendering, fetching, and setting the state with setStories(tales), which triggers another render, starting the cycle all over again. Luckily, React gives us a way of treating useEffect() similar to componentDidMount().

If we pass useEffect() an empty array as a second argument, the function passed as the first argument only executes upon the first render. This means no more infinite loop for us. Even though we trigger another render when we set the state, this useEffect() doesn’t execute its code again. This wonderfully easy fix takes just three characters ( ,[] ) to save us from our endless cycle of re-rendering. Here it is in action with the rest of the useEffect():

Notice the tiny difference from the other

What's happening here? What is this empty array we pass as a second argument to useEffect() and why does it stop the useEffect() from executing after the first time.

Dependency Array

The second argument that useEffect() accepts is known as a dependency array. If any of the dependency array’s elements change between renders, useEffect() executes. When we pass an empty dependency array, there are no elements that could change resulting in the function executing again.

The empty dependency array is a cheap fix that results in useEffect() acting similar to ComponentDidMount(). It is not recommended to use an empty dependency array, because it can result in unexpected bugs. Instead of using an empty dependency array, use an element in the dependency array that doesn’t change every time useEffect runs.

You might think our state hook variable stories could be a good option to use, because the data we get from the fetch shouldn’t change. However we have to declare the variable outside the useEffect() function, so it looks like it changes every time useEffect() runs since it’s comparing the value declared after the fetch with the empty array assigned to the variable upon declaration. In this situation, I used the url pathname as the array dependency element, because this component will unmount anyway when the pathname changes.

There are plenty of times you’ll utilize the array dependency in a more useful way, but this is an easy introduction to the concept. If you’re looking for more than just an introduction to array dependencies with useEffect(), check out these articles:

--

--