DEV Community

PRIYA K
PRIYA K

Posted on

useEffect in React Hooks

Is a React Hook
Allows you to synchronise a component with an external system.
Used for Tasks that happen outside the normal rendering system,such as fetching data,setting up subscriptions,manually manipulate the DOM
Allows you to perform side effects in your components.
useEffect accepts two arguments. The second argument is optional.
useEffect(, )
We should always include the second parameter which accepts an array. We can optionally pass dependencies to useEffect in this array
In React, useEffect runs after every render by default, so your (settimer)timer keeps repeating.
To control it, use a dependency array: [] → run only once, [count] → run when count changes.
Without dependencies, it keeps re-running and causes continuous updates.
Always add dependencies properly to make your Effect run only when needed.
It replaces lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount in class components.
The first argument to useEffect is a function that contains the side effect code in functional components.
The second argument is an optional array of dependencies. If provided, the effect will only re-run if any of the dependencies change.
Manipulating the DOM directly (although React generally handles DOM manipulation for you).
Cleaning up resources when a component unmounts.

Syntax:

useEffect(() => {
    // Code to run on each render
    return () => {
        // Cleanup function (optional)
    };
}, [dependencies]);
Enter fullscreen mode Exit fullscreen mode

Effect function: side effect code runs.
Cleanup function: This optional return function cleans up side effects like subscriptions or timers when the component unmounts.
Dependencies array: React re-runs the effect if any of the values in this array change.

Official Use Cases & Recommendations
External Synchronization: Use it to connect to a chat server, control a non-React widget, or listen to browser events.
Data Fetching: While common, the Official React Documentation recommends using framework-specific tools or client-side caches (like TanStack Query) instead of manual Effects to avoid "waterfalls" and race conditions.
Avoid Overuse: Do not use Effects for logic that can be handled during rendering, such as transforming data or handling user events (which should remain in event handlers

Working of useEffect
In React, useEffect runs after the component renders on screen.
It runs every time, once, or only on changes depending on the dependency array.
Before running again (or removing the component), it runs a cleanup function.
It helps handle tasks like API calls, timers, or events using state and props easily.

Ways to mimic lifecycle methods using useEffect hook
The useEffect() hook is not only used for handling side effects, but it also allows functional components to replicate the behavior of class-based lifecycle methods like

Mimicking componentDidMount: To run code once when the component mounts, pass an empty dependency array ([]) to useEffect. This ensures the effect runs only once, similar to componentDidMount.

useEffect(() => {
    console.log("Component mounted (Functional)");
}, []);
Enter fullscreen mode Exit fullscreen mode

Mimicking componentDidUpdate: To run code every time a specific state or prop changes, include those variables in the dependency array. This simulates componentDidUpdate.

useEffect(() => {
    console.log("Component updated (Functional)");
}, [value1, value2]);
Enter fullscreen mode Exit fullscreen mode

Mimicking componentWillUnmount: To run cleanup logic when the component unmounts, return a cleanup function from useEffect. This simulates componentWillUnmount.

useEffect(() => {
    return () => {
        console.log("Component unmounted (Functional)");
    };
}, []);

Enter fullscreen mode Exit fullscreen mode

Effect Cleanup
Some effects require cleanup to reduce memory leaks.
Timeouts, subscriptions, event listeners, and other effects that are no longer needed should be disposed.
We do this by including a return function at the end of the useEffect Hook.

Cleaning Up Side Effects
useEffect allows you to clean up after your effects by returning a cleanup function. This is particularly useful for:

Removing event listeners.
Canceling network requests.
Clearing timers or intervals.

Example:
Clean up the timer at the end of the useEffect Hook:

import { useState, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
function Timer() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    let timer = setTimeout(() => {
      setCount((count) => count + 1);
    }, 1000);
    return () => clearTimeout(timer)
  }, []);
  return <h1>I've rendered {count} times!</h1>;
}
createRoot(document.getElementById('root')).render(
  <Timer />
);

Enter fullscreen mode Exit fullscreen mode

SYNTAX

useEffect(setup,dependencies?)
Enter fullscreen mode Exit fullscreen mode

1. No dependency passed:

useEffect(() => {
  //Runs on every render
});
Enter fullscreen mode Exit fullscreen mode

2. An empty array:

useEffect(() => {
  //Runs only on the first render
}, []);
Enter fullscreen mode Exit fullscreen mode

3. Props or state values:

useEffect(() => {
  //Runs on the first render
  //And any time any dependency value changes
}, [prop, state]);
Enter fullscreen mode Exit fullscreen mode

setup:
A function containing effect logic.
optionally it can return a Cleanup function to undo the effect(eg..closing the connection)
When component commits,React will run your Setup function
After every commit with changed dependencies,React will first run the cleanup function with the old values ,Run setup function with new values
After the component is removed from the DOM,React will run the cleanup Function.

dependencies:
An optionl array of reactive values(props,state,variable,functions declared directly inside the component body) inside the setup code , tiggers the effect when changed.
A list of dependencies[dep1,dep2] must have constant number of items.
React will compare each dependency with its previous value using the object.is comparison.
If you omit this argument ,Effect will re-run after every commit of component

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, roomId]);
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Returns
useEffect returns undefined

Timing and Execution
React runs the effect after the component has rendered and it updates the screen.
No dependency Array:Runs after every render
Empty dependency Array[]:Runs only once,after the initial mount.
With dependencies[dep1,dep2]: Runs on initial mount,any list value changes.

Caveats
Call useEffect at the top of your component to declare an effect
Can't call inside loops or conditions.
Exact the new components and move the state into it.
you may not trying to synchronise a component with an external system,don't need an effect.

Strict Mode: React will run setup and cleanup one extra time to help find bugs.
1.Strict Mode is on,React runs your code twice in development to test it.
It checks if your setup and cleanup are working properly.
Cleanup should undo everything your setup did(like removing timers or events)
If errors happen,fix your cleanup function.

2.if you put objects or functions inside the component,change every time it renders.
This makes your Effect run again and again,even when it's not needed.
To avoid this ,Don't include unnecessary objects or functions as dependencies.
Move extra logic or state updates outside the effect,so it runs only when needed

3.In React, useEffect runs after the screen updates and shows to the user.
So if your effect changes something visual, users might briefly see a wrong position (like flicker).
useLayoutEffect runs before the screen is shown, so changes happen instantly.
Use useLayoutEffect only when you need smooth visual updates (like positioning elements).

4.In React, if an Effect is triggered by a user action (like a click), it may run before the screen updates.
This helps React handle events properly and show correct results.
Most of the time, this behavior is fine and works as expected.
If you want to delay it until after the screen updates (like showing alert()), use setTimeout.

5.In React, even after a click, useEffect might run after the screen updates.
This is usually fine and users won’t notice any issue.
If you need to stop the screen from updating until your code runs, use useLayoutEffect.
Effects run only in the browser, not during server-side rendering.

Infinite Loop: updating a state variable,also listed as dependency can cause a loop re-renders.
Client-Only: Effect only run in the browser and do not execute during server rendering.

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, roomId]);
  // ...
}

Enter fullscreen mode Exit fullscreen mode

Usage
Connecting to an external system
In React, useEffect is used to connect your component to things outside React (like APIs, timers, or events).These systems are not connected to React ,so they are called external.

To connect your component to some external system, call useEffect at the top level of your component:
You write setup code (start connection) and a cleanup function (stop/disconnect).
React runs setup when the component starts, and runs cleanup(with the old props and state) + setup(with the new props and state) again when values change.
When the component is removed, cleanup runs one last time (to avoid memory leaks),after your component is removed from the page (unmounts).
If you’re not using anything external (like API, timer, event), you usually don’t need useEffect.
A list of dependencies including every value from your component used inside of those functions.
Try to write Every Effect as an independent process and single setup/cleanup cycle at a time.
component is mounting, updating, or unmounting.

In React, when the component loads, it connects (setup). When values change, it disconnects old + connects new. When it’s removed, it disconnects finally.
In development, React runs setup → cleanup → setup once extra to check for bugs.
Your cleanup should undo everything setup did, so users don’t notice any difference.
Examples of external systems: API/chat connection, timers (setInterval), events (addEventListener), animations.

A timer managed with setInterval() and clearInterval().
An event subscription using window.addEventListener() and window.removeEventListener().
A third-party animation library with an API like animation.start() and animation.reset().

In React, Effects are used when you need to do things outside React.
If you write the same useEffect logic again and again, you should create a custom Hook.
A custom Hook reuses and hides complex logic, making your code cleaner.
Example: useChatRoom handles connection logic so your component stays simple.

Controlling a non-React widget
In React, you can use useEffect to control things not built with React (like maps or video players).
When your React state (like zoom level) changes, the Effect updates the external widget.
This keeps both React and the external system in sync.
Sometimes cleanup isn’t needed if the browser automatically removes everything.

Fetching data with Effects
In React, you can use useEffect to fetch data from an API.
The ignore flag helps avoid bugs when responses come in the wrong order (race condition).
You can use async/await, but still need cleanup to handle updates safely.
Writing fetch logic in Effects again and again is messy—better use a custom Hook or framework method.

useEffect(() => {
  const fetchData = async () => {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    setData(data);
  };

  fetchData();
}, [props.someProp]);
Enter fullscreen mode Exit fullscreen mode

Specifying reactive dependencies
In React, you must include all reactive values(like props or state) used inside useEffect as dependencies .
If those values change, the Effect will run again automatically.
If a value never changes (not reactive), you can remove it from dependencies. = empty dependencies
An Effect with empty dependencies doesn’t re-run when any of your component’s props or state change.
Don’t ignore the linter—missing dependencies can cause bugs and wrong behavior.
If you specify the dependencies, your Effect runs after the initial commit and after commits with changed dependencies.
If your Effect truly doesn’t use any reactive values, it will only run after the initial commit.
If you pass no dependency array at all, your Effect runs after every single commit of your component.

Updating state based on previous state from an Effect
In React, count is a reactive value, so normally it must be in list of dependencies.
That makes the Effect setup and cleanup every time count changes.
Using c => c + 1 updates state based on the previous value instead of count + 1, so it doesn’t depend on count anymore.
This avoids unnecessary re-running, making your Effect more efficient

Removing unnecessary object dependencies
In React, objects or functions created inside a component are new on every render.
If you use them as dependencies, useEffect will run again and again unnecessarily.
To fix this, create that object inside the Effect instead of outside.
Then the Effect depends only on real values (like roomId), so it runs only when needed.

Reading the latest props and state from an Effect
In React, normally you must add all reactive values as dependencies so the Effect updates correctly.
sometimes you’ll want to read the latest props and state from an Effect without “reacting” to them.
But sometimes you want to use a value without re-running the Effect when it changes.
For that, use useEffectEvent and move that code inside it.
This way, your Effect runs only when needed (like url), but still reads the latest data (like shoppingCart).

Displaying different content on the server and the client
In React, apps can render on both server and client, and the first output should be the same.
Some things (like localStorage) work only on the client, not on the server.
So you use useEffect to update after loading, which causes a re-render on the client.
Use this carefully, because users may briefly see different content before it updates.

Subscribing to Events:
Subscribe to events such as scrolling, resizing, or keyboard events.

useEffect(() => {
  const handleScroll = () => {
    // Handle scroll event
  };
  window.addEventListener('scroll', handleScroll);
  return () => {
    window.removeEventListener('scroll', handleScroll);
  };
}, []);
Enter fullscreen mode Exit fullscreen mode

Cleaning Up:
Perform cleanup tasks like unsubscribing from events or canceling async tasks.

useEffect(() => {
  const subscription = someObservable.subscribe();

  return () => {
    subscription.unsubscribe();
  };
}, []);
Enter fullscreen mode Exit fullscreen mode

Questions
1.A useEffect hook depends on [count, userId]. In which situation will the effect re-execute?
Answer
When either count or userId changes
2.Why is the cleanup function executed before an effect runs again after dependency changes?
Answer
To remove previously created side effects like subscriptions or timers

3.A developer wants to simulate the behavior of componentWillUnmount in a functional component. Which useEffect pattern correctly achieves this?
Answer:
useEffect(() => { return () => console.log("Cleanup"); }, []);

4.A component uses:
useEffect(() => {
console.log("Effect executed");
});
How will this effect behave during the component lifecycle?
Answer:
It runs after every render

5.Which situation will NOT trigger the cleanup function of a useEffect hook?
Answer:
During the initial render

6.In React, what is the purpose of the cleanup function in the useEffect hook?
Answer:
To clean up any subscriptions or side effects

Top comments (0)