Home/Blog/Programming/Advanced React Hooks: Deep Dive into useEffect Hook
Home/Blog/Programming/Advanced React Hooks: Deep Dive into useEffect Hook

Advanced React Hooks: Deep Dive into useEffect Hook

9 min read
Oct 23, 2023
content
What are React Hooks?
Class implementation vs. Hook implementation
The useEffect Hook explained
Syntax for useEffect Hook
Example 1: useEffect with no dependency array
Example 2: useEffect with an empty dependency array
Example 3: useEffect with specific dependencies
Summary of useEffect dependency array configuration
Multiple useEffect hooks
Cleanup works in useEffect
Common pitfalls of useEffect
Best practices for useEffect
Continue learning React Hooks

Become a Software Engineer in Months, Not Years

From your first line of code, to your first day on the job — Educative has you covered. Join 2M+ developers learning in-demand programming skills.

Key Takeaways:

  • The useEffect Hook enables you to perform side effects such as data fetching, DOM updates, and event handling in functional components.

  • Updating state inside useEffect without proper dependencies can cause infinite re-renders. So it is advisable to always include all state and props used in the effect in the dependency array.

  • The useEffect Hook is ideal for data fetching from APIs, manipulating the DOM, managing timers, and handling browser events like window resizing.

  • Hooks allow functional components to handle state and lifecycle methods, providing a simpler and more reusable code structure compared to class components.

What are React Hooks?#

React is a JavaScript library for designing a web application’s user interface. It uses a component-like structure, where a web page is split into multiple components. Hooks were introduced in React version 16.8. With the help of these Hooks, we can reuse stateful logic and manage a component's lifecycle.

React Hooks are functions that enable developers to manage state and lifecycle features in function components, providing an alternative to class components for these purposes while maintaining backward compatibility with classes.

React provides several built-in Hooks that cover a wide range of functionalities:

  • useState: Manages state within functional components.

  • useEffect: Handles side effects such as data fetching or subscriptions.

  • useContext: Access context values directly, eliminating the need for a separate consumer component, not the Context API itself.

  • useReducer: Manages complex state logic, offering an alternative to useState.

  • useMemo and useCallback: Optimize performance by avoiding unnecessary recalculations and function recreations, as well as by memoizing values and functions.

Cover
Learn React Hooks for Frontend Development

Web development has changed significantly in recent years. A web developer should not only know the basics of HTML, CSS, and JavaScript but also be proficient in frameworks such as Angular, React, and Vue. These frameworks make web development easier, enabling developers to focus on the application logic. This course is designed for newcomers to React and experienced React developers alike. You’ll start by learning the most fundamental concept in React—components. Specifically, you’ll learn about function components. You’ll then learn how to manage application state and side effects. You’ll then learn about the useMemo hook to boost an application’s performance. You’ll learn about the useContext hook which enables us to perform area updates in our application. Finally, you’ll learn how to create custom hooks in React. After completing this course, you can develop frontend applications with React. You’ll be able to develop complex web applications faster because React makes the process much easier.

6hrs
Intermediate
78 Playgrounds
9 Quizzes

Here are some advantages of React Hooks:

  • Better code composition: Hooks allow lifecycle methods to be written in a linear, render-flowing order rather than splitting them among relevant Class Components.

  • Reuse states and components: Hooks make it easier to share stateful logic between different components. Hooks allow you to reuse stateful logic between components by encapsulating it in custom Hooks. Each component instance still has its own isolated state.

  • Better testing: Hooks consolidate stateful logic so it’s all defined in a relevant Hook and is, therefore, easier to test.

  • Performance: When optimized, React Hooks allow functional components to perform efficiently, leveraging modern React features.

Class implementation vs. Hook implementation#

When looking at older React legacy code, most likely, you will see something like the following:

The code above uses the componentDidMount method and this.setState to reference and manipulate the posts. This functionality can be replaced by the useEffect and useState Hooks.

To convert the code into a functional component, we’ll:

  • Use the useState Hook to manage the posts state

  • Replace componentDidMount method with the useEffect Hook

  • Set the posts state using the setter function provided by useState Hook

Here’s what the same React code looks like using Hooks:

The useEffect Hook explained#

Our focus will be on the useEffect Hook. It is one of React’s built-in Hooks that allows you to perform side effects in React functional components when the application mounts or when the state or prop gets updated. A side effect could include:

  • Data fetching from an external API.

  • Manipulating the Document Object Model (DOM).

  • Using a timer function like setTimeout() that runs for a certain period.

Let’s take a deeper look at the useEffect Hook to understand how that works. Think of the useEffect Hook as a partial replacement for React lifecycle events. The useEffect Hook can replicate the behavior of componentDidMount, componentDidUpdate and componentWillUnmount methods.

In other words, you can respond to changes in any component that contains the useEffect Hook.

Practice the useEffect Hook with hands-on experience with this project, Build a Memory Game Using React.

Syntax for useEffect Hook#

The syntax to define the useEffect Hook is as follows:

useEffect(callbackFunction, [dependencies]);
Syntax for the useEffect Hook

In the syntax above, we have the following:

  • A callback function, callbackFunction, contains the logic that will run when the component renders or updates.

  • An optional dependency array, [dependencies]. This array determines when the effect should be re-run. If it’s empty, the effect runs only once on the component mount. If state or props are listed, the effect runs whenever they change.

Note: The dependency array is optional, but the way you use it significantly affects how your component behaves. For example:

  • If you provide an empty dependency array, [], then the effect only runs once on the component’s initial render.

  • If specific dependencies, [dependency1, dependency2], are provided then the effect runs only when a value in the dependency array changes.

  • If no dependency array is provided then the effect will run after every render.

The useEffect Hook can be used in different ways to achieve different results depending on the use-cases. Let's look at some of the examples by which we can use the Hook.

Example 1: useEffect with no dependency array#

If no dependency array is provided, the effect will execute every time the component renders. This includes both initial render and subsequent re-renders triggered by state updates.

In the example below, every time the component renders, useEffect adds a new post to the posts state. This will cause an infinite loop because updating the state (setPosts) triggers a re-render, which in turn triggers useEffect again, continuing indefinitely. You see the "useEffect without dependency array called" message being printed in the console an infinite number of times.

It's important for developers not to use setPosts or other state-modifying functions inside a useEffect with no dependency array unless it's part of an intentional design pattern, like accumulating posts based on certain conditions.

Probable outcomes are that the browser may crash or the application may become unresponsive due to the continuous state updates.

Example 2: useEffect with an empty dependency array#

An empty array [] ensures the effect only runs once after the initial render of the component. This is useful for fetching data on mount, setting up event listeners, or initializing third-party libraries.

In the example below, useEffect sets the posts state to a single post when the component mounts. Upon the first render, the posts state is updated to include "Post 1". Subsequent renders do not trigger useEffect, so no additional posts are added.

Explore the useEffect Hook by implementing it in a real world use case in this project, Build the Frontend of a Financial Application Using React.

Example 3: useEffect with specific dependencies#

The useEffect Hook with [filter] as its dependency array runs only when the filter state changes. In this example, as the user types into the filter input, the filter state updates, triggering useEffect to filter the mockPosts based on the current filter value.

In the example below, the dependency array filter ensures that useEffect runs only when the filter state changes. Initially, the filter is empty, so all posts are displayed based on the filtering logic. As the user types (e.g., "React"), the list updates to show only posts with titles that include "React".

Apply the useEffect hook in a real-world scenario by working on this project, Build an Image Sharing App with MERN Stack.

Summary of useEffect dependency array configuration#

Let's look at the main differences with using the dependency array in the table below:

Configuration

Run When

Use Cases

Potential Pitfalls

No dependency array

After every render

Debugging, logging every render

Risk of infinite loops if the effect modifies state.

Empty dependency array

Only once after the initial render

Initialization, fetching data on mount

Subsequent changes to dependencies are ignored.

Specific dependencies

When specified dependencies change

Responsive side effects, derived state, conditional logic

Risk of stale closures if dependencies aren’t correctly specified.

Multiple useEffect hooks#

When working with useEffect, it is a best practice to use multiple hooks for different side effects rather than combining unrelated logic into a single useEffect. This approach makes your code cleaner and easier to understand and simplifies debugging and testing.

  • Separation of Concerns: Assign each useEffect to a specific task, like data fetching or event handling, for modular code.

  • Better Dependency Management: Use separate dependency arrays to ensure effects trigger only when relevant variables change.

  • Improved Debugging: Isolate effects to quickly identify and fix issues in specific hooks.

  • Enhanced Readability: Simplify understanding and maintenance by keeping each useEffect focused on a single responsibility.

In the above example, two useEffect hooks are used, each serving a distinct purpose:

  1. First useEffect: Filters the posts based on the filter value. It updates the posts state whenever the filter changes. This isolates the filtering logic and avoids unnecessary side effects tied to other state changes.

  2. Second useEffect: Logs the posts data to the console every time the posts state changes. This is a separate concern, specifically for logging purposes.

Cleanup works in useEffect#

React provides a way to handle cleanup by returning a cleanup function inside the useEffect callback. This cleanup function is executed when:

  • The component is about to unmount (e.g. when the user navigates away).

  • Before the effect runs again (if the dependencies change).

This ensures that the resources are freed up appropriately, helping to prevent memory leaks.

The cleanup part in the useEffect hook removes the resize event listener when the component unmounts, preventing potential memory leaks by ensuring the listener is no longer active after the component is removed from the DOM.

Common pitfalls of useEffect#

Here are some common pitfalls of useEffect:

  • When using variables or functions inside a useEffect hook, React may capture stale values from previous renders if the dependency array is not properly defined. This happens because the closure of a function in the effect may reference the value present when the effect was last executed.

  • React compares dependencies by reference for objects and arrays. This means that objects or arrays will always be treated as "new" if they are re-created during each render, causing the effect to run unnecessarily.

  • Sometimes, developers accidentally omit variables used inside the dependency array's effect, leading to unexpected behavior or bugs.

Best practices for useEffect#

  • Minimize effects: Keep your useEffect logic concise and focused. Avoid including heavy computations or unnecessary operations that can impact performance. For complex logic, consider breaking it into smaller custom hooks.

  • Use Dependency arrays wisely: Always specify a proper dependency array to ensure useEffect runs only when intended. Avoid direct state updates inside useEffect without declaring the relevant dependencies to prevent infinite loops.

  • Avoid side effects in render: Do not include state updates or asynchronous logic directly within the render method. Instead, use useEffect for such operations to maintain a clean and predictable render cycle.

  • Debugging tips: Use console.log statements to inspect values within useEffect and track their execution. Leverage React Developer Tools Profiler to analyze component re-renders and measure the performance of your effects.

  • Prefer Cleanup Functions: If your effect involves setting up event listeners, subscriptions, or timers, always include a cleanup function to avoid memory leaks or unintended behaviors.

Continue learning React Hooks#

Explore these projects for hands-on practice with React Hooks to deepen your understanding of how Hooks work and gain practical experience.

Frequently Asked Questions

What are common debugging techniques for useEffect?

To debug useEffect, use console.log statements, ensure correct dependencies, profile re-renders with React DevTools, and isolate the hook to pinpoint its impact on the component’s behavior.

How does React batch state updates within useEffect?

When should I use useLayoutEffect instead of useEffect?

What happens if I omit the dependency array in useEffect?

Can useEffect replace all lifecycle methods?


Written By:
Ryan Thelin
Join 2.5 million developers at
Explore the catalog

Free Resources