One of the core fundamentals beginner coders learn is that functions are "self-contained" blocks of reusable code that perform a single task. Functions "take in" data, processes it, and "return" a result, and can be run multiple times. But what if they could do more? What if they could have a side effect? Let me introduce React's useEffect
hook.
Hooks
If you're new to React, like I was a month and a half ago, you might not be too familiar with hooks, yet. Hooks lets you add stateful logic to a functional component without writing a class. There are several types of hooks built into React including, but not limited to: Effect, Context, and State. useEffect
falls under the Effect hook, hooks that allow components to connect and synchronize with external systems such as the browser DOM, network, and other non-React code.
But why would we need to do that? In the realm of React development, managing side effects is an essential aspect of building robust and efficient applications. Here are a few examples for you. What if we have an object that we want to use to control a non-React component based on the object's State? Or what if we need to perform a GET request right after the page is fully rendered? How about setting up a timer that only increments while it is active? Or simply being able to subscribe to events or update the DOM? These scenarios, crucial for creating dynamic user interfaces, are easily achievable with the useEffect
hook.
useEffect Basic Usage
Before we begin, don't forget to import the useEffect
hook!
Now, let's start with the basics. The useEffect
hook accepts two parameters: a callback function and an array of dependencies. The function is the side effect we want to perform, and the dependencies array allows us to specify values that, when changed, will trigger the effect to run again. This mechanism ensures precise control over when the side effect should be executed.
That is why understanding how dependencies work in useEffect
is key for writing efficient and bug-free code. When we provide a dependency array, React will re-run the effect whenever any of the dependencies change. If you omit the dependency array, the effect will run on every render, potentially causing performance issues. Imagine the side effect code running in an infinite loop, an unstoppable force littering your console with lines of GET request arrays or "Hello, World!". Scary. That's why, at the minimum, we should always supply the useEffect
hook with an empty dependency array. We can also provide arrays that aren't empty, and these will ensure that the effect will only run when the contents of the array change.
If we provide multiple arrays, such as dependency1
and dependency2
, the effect will read this as run when dependency1
or dependency2
changes. And these arrays usually change when we setState
of a component and it re-renders. By default useEffect
will run the side effect function in the following order:
By following these dependency array rules, we can optimize performance and avoid unnecessary computations.
Cleaning Up
Another neat feature of the useEffect
hook is that it is designed to allow the 'return' of a function within it as a means to tidy up our code before the component unmounts. This function also runs right before the execution of the next effect. This is because specific side effects might require cleanup to prevent memory leaks or unwanted behavior.
For example, let's say you're performing a fetch request on the server to get user A's information but change your mind before the request is complete. Instead, you decide to fetch the information of user B. Without the cleanup function, both fetch requests would continue to run even though the components have been unmounted or the dependencies have changed. This can lead to unexpected behaviors such as the effect attempting to update components that are no longer mounted or display the wrong information. With the cleanup function, we can abort the first fetch request of user A before moving on to user B and ensure that resources are properly released when they're no longer needed.
The following is another example with corresponding code:
In this example, the useEffect
hook sets up an interval that increments count
every second. The clean-up function returned by useEffect
clears the interval using `clearInterval(intervalID) which stops the interval from running.
Conclusion
In conclusion, the useEffect
hook is an incredibly essential tool for managing side effects in React functional components. It's versatile and simple and can handle a wide range of scenarios such as fetching data, subscribing to events, and interacting with the DOM. Although we shouldn't use this effect to handle every aspect of our code like orchestrating the data flow of our apps, it is a great hook that lets us "step out" of our React code and synchronize with external systems like API's, databases, networks, and so on. So go ahead, leverage the power of useEffect
for your projects, and embrace this wonderful React hook.
Top comments (2)
Hi, thank you for the post! Can you explain "Cleaning Up" part more? Can you provide some examples? Does it mean that you need to do it every time you use useEffect()? Maybe you could do a separate post about it.
Also, nice memes
Definitely! I'll be revisiting this post in the coming days to expand on that section and provide an example!
The cleanup function is optional to include in your useEffect callback function block; however, it is recommended to avoid things like memory leaks. If you do include the cleanup function, it will run every time useEffect runs.