| Too Many Re-renders in React :👈 | 👉:Fix Maximum Update Depth Exceeded |
useEffect executes twice |
useEffect executes twice in React occurs when the component is mounted and then immediately re-mounted, or when the component is updated with new props or state that cause the effect to be re-run.
Why Does useEffect Run Twice in React?
1. Quick Answer
React's useEffect may run twice during development because React Strict Mode intentionally performs an additional Effect setup and cleanup cycle to help identify bugs.
This behavior commonly occurs when your application is wrapped in <React.StrictMode> and an Effect uses an empty dependency array.
Example:
import { useEffect } from "react";
function App() {
useEffect(() => {
console.log("Effect executed");
return () => {
console.log("Effect cleanup");
};
}, []);
return <h1>Hello React</h1>;
}
export default App;
In development with Strict Mode enabled, the initial Effect lifecycle commonly produces the following console output:
Effect executed
Effect cleanup
Effect executed
The extra setup and cleanup cycle helps verify that your Effect handles cleanup correctly.
Important: This is development-only behavior associated with Strict Mode. In a normal production build, React does not perform this additional Strict Mode Effect cycle.
2. Table of Contents
- Concept Explanation
- Diagram
- Real-World Example
- Code Examples
- Common Mistakes
- When to Use It
- When NOT to Use It
- Interview Questions
3. Concept Explanation
3.1 What Is useEffect?
useEffect is a React Hook used to synchronize a component with an external system.
Common examples include:
- Fetching data from an API.
- Subscribing to events or data streams.
- Setting up timers.
- Connecting to a WebSocket.
- Registering browser event listeners.
- Synchronizing with third-party libraries.
Basic syntax:
useEffect(() => {
// Setup or synchronization logic
return () => {
// Cleanup logic
};
}, [dependencies]);
The dependency array determines when React needs to re-synchronize the Effect.
3.2 Why Does useEffect Run Twice?
The most common explanation is React Strict Mode in development.
Strict Mode helps identify problems such as:
- Missing cleanup functions.
- Duplicate subscriptions.
- Timers that are not cleared.
- Effects that incorrectly assume setup happens only once.
- Side effects that are not safe to repeat.
When Strict Mode is enabled, React performs an additional development-only setup and cleanup cycle for Effects when a component mounts.
This behavior helps verify that an Effect can start, clean up, and start again correctly.
3.3 Understanding the Empty Dependency Array
Consider this code:
useEffect(() => {
console.log("Component mounted");
}, []);
The empty dependency array means the Effect has no reactive dependencies that should cause it to re-synchronize when their values change.
It does not mean the Effect is guaranteed to execute exactly once in every development environment. Strict Mode may perform the additional setup and cleanup cycle.
3.4 How the Dependency Array Affects useEffect
| Dependency Pattern | When the Effect Runs |
|---|---|
| No dependency array | After every committed render. |
Empty array: [] |
After the initial mount, with an additional development-only setup and cleanup cycle when applicable under Strict Mode. |
Dependencies: [userId] |
After the initial mount and when userId changes according to React's dependency comparison. |
Multiple dependencies: [userId, page] |
After the initial mount and when either dependency changes. |
React compares each dependency with its previous value using Object.is.
3.5 Is useEffect Running Twice Always a Bug?
No. If the extra execution occurs because of Strict Mode during development, it is expected behavior.
However, repeated Effect execution may indicate a problem when:
- The Effect runs after every render because the dependency array is missing.
- A dependency changes on every render.
- The Effect updates state and causes a repeated update cycle.
- Cleanup is missing or incorrect.
- The component genuinely unmounts and mounts again.
The important task is to identify which situation is causing the repeated execution.
4. Diagram
4.1 useEffect with Strict Mode Enabled
This simplified diagram represents the extra development-only Effect lifecycle check performed by Strict Mode on initial mounting.
4.2 useEffect Without the Extra Strict Mode Cycle
When a dependency changes, React cleans up the previous Effect before running the new setup. When the component is removed, React runs the cleanup function for the active Effect.
5. Real-World Example
Scenario: Fetching Customer Details
Imagine an application that fetches customer information from an API when the customer details component loads.
The developer writes the following code:
import { useEffect } from "react";
function CustomerDetails() {
useEffect(() => {
fetch("/api/customers/101")
.then(response => response.json())
.then(customer => {
console.log(customer);
});
}, []);
return <h2>Customer Details</h2>;
}
export default CustomerDetails;
When this component mounts under development Strict Mode, the Effect setup can run twice. Consequently, the application may send two requests.
Why can this be a problem?
- The API may receive duplicate requests.
- The browser may perform unnecessary network work.
- The application may process the same response more than once.
- Repeated requests can become expensive when the API performs costly operations.
For a read-only request, duplicate requests are usually a performance concern. For an operation that changes data, such as creating an order, duplicate execution can have more serious consequences.
Recommended Approach
Write Effects so that their setup and cleanup are safe to repeat. For a data-fetching Effect, use an appropriate cancellation or stale-result prevention strategy.
import { useEffect, useState } from "react";
function CustomerDetails() {
const [customer, setCustomer] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
async function loadCustomer() {
try {
const response = await fetch(
"/api/customers/101",
{ signal: controller.signal }
);
if (!response.ok) {
throw new Error("Failed to load customer");
}
const data = await response.json();
setCustomer(data);
} catch (err) {
if (err.name !== "AbortError") {
setError(err.message);
}
}
}
loadCustomer();
return () => {
controller.abort();
};
}, []);
if (error) {
return <p>Error: {error}</p>;
}
if (!customer) {
return <p>Loading customer...</p>;
}
return <h2>{customer.name}</h2>;
}
export default CustomerDetails;
How does this help?
- An
AbortControlleris created for each Effect setup. - The request receives the controller's abort signal.
- When React cleans up the Effect, the associated request is aborted if it is still in progress.
- The code ignores the expected abort error.
- Successful requests update the customer state.
Important: Aborting a request does not guarantee that the server never received or processed it. For data fetching, consider framework-provided data loading or caching mechanisms when appropriate. For operations that change server data, use a design that handles retries and duplicate requests safely.
6. Code Examples
6.1 Example: Effect Runs Twice in Development
import { useEffect } from "react";
function App() {
useEffect(() => {
console.log("Effect started");
return () => {
console.log("Effect cleaned up");
};
}, []);
return <h1>Hello React</h1>;
}
export default App;
With development Strict Mode enabled, the initial Effect lifecycle typically logs:
Effect started
Effect cleaned up
Effect started
The cleanup between the two setup calls is the important detail. React is testing whether the Effect can be safely restarted.
6.2 Example: Enabling Strict Mode
In a typical React application, Strict Mode may be enabled in the entry point:
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
createRoot(document.getElementById("root")).render(
<StrictMode>
<App />
</StrictMode>
);
With Strict Mode enabled, React performs additional development checks. One of these checks is the extra Effect setup and cleanup cycle on initial mounting.
Strict Mode is recommended because it helps reveal bugs before production.
6.3 Example: useEffect Runs After Every Committed Render
import { useEffect, useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Effect executed");
});
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
export default Counter;
Explanation: Because no dependency array is provided, the Effect runs after each committed render. Clicking the button changes the state, causing another render and another Effect execution.
This behavior is not necessarily an infinite loop because the Effect itself does not update the state.
6.4 Example: An Effect Causes an Infinite Update Loop
import { useEffect, useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1);
});
return <p>Count: {count}</p>;
}
export default Counter;
Explanation:
- The component renders.
- The Effect runs and updates
count. - The state update triggers another render.
- The Effect runs again because it has no dependency array.
- The cycle repeats until React stops the update loop.
Correct approach: Remove the unnecessary Effect or redesign the synchronization logic so the Effect does not repeatedly trigger its own dependencies.
6.5 Example: An Effect Depends on a Function Created During Rendering
import { useEffect } from "react";
function UserProfile({ userId }) {
function loadUser() {
console.log("Loading user:", userId);
}
useEffect(() => {
loadUser();
}, [loadUser]);
return <p>User ID: {userId}</p>;
}
Problem: A new loadUser function is created on each render. Because its reference changes, React treats the dependency as changed and runs the Effect again after the next committed render.
Better approach: Define the function inside the Effect when it is only needed there.
import { useEffect } from "react";
function UserProfile({ userId }) {
useEffect(() => {
function loadUser() {
console.log("Loading user:", userId);
}
loadUser();
}, [userId]);
return <p>User ID: {userId}</p>;
}
Now the Effect depends on userId rather than a function reference that changes on every render.
6.6 Example: Preventing Duplicate Event Listeners
Effects that register browser event listeners should remove them during cleanup.
import { useEffect } from "react";
function WindowWidthTracker() {
useEffect(() => {
function handleResize() {
console.log(window.innerWidth);
}
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return <p>Resize the browser window.</p>;
}
export default WindowWidthTracker;
Why is cleanup important?
- It removes the event listener when the Effect is cleaned up.
- It prevents old listeners from remaining active after cleanup.
- It allows React to safely repeat the Effect setup during development Strict Mode checks.
6.7 Should You Disable Strict Mode?
You can change Strict Mode configuration, but disabling it just to avoid seeing an Effect execute twice is generally not the recommended solution.
Instead:
- Keep Effects safe to start and stop.
- Add cleanup for subscriptions, timers, and event listeners.
- Handle asynchronous operations and stale results correctly.
- Use event handlers for actions that should happen only after a user interaction.
- Use appropriate server-side safeguards for operations that modify data.
7. Common Mistakes
| Mistake | Why It Is Wrong | Recommended Solution |
|---|---|---|
| Assuming useEffect must always run exactly once | Strict Mode may perform an additional setup and cleanup cycle during development. | Make the Effect safe to restart. |
| Removing Strict Mode immediately | Hides development checks without fixing the underlying issue. | Investigate the Effect's setup and cleanup behavior. |
| Forgetting to remove event listeners | Old listeners can remain active and cause repeated handling. | Remove listeners in the cleanup function. |
| Forgetting to clear timers | Timers may continue running after cleanup or unmounting. | Use clearInterval or clearTimeout as appropriate. |
| Updating state unconditionally inside an Effect without dependencies | Can create an infinite rendering loop. | Correct the state-update logic and dependencies. |
| Ignoring Effect dependencies | May cause stale values or repeated synchronization. | Declare the required dependencies and restructure the Effect when necessary. |
| Using a ref as a universal way to skip the second Effect execution | Can hide missing cleanup and break when components remount. | Make setup and cleanup correct instead of suppressing the check. |
| Assuming two API requests always indicate Strict Mode | Requests may also be duplicated by remounting, retries, or application logic. | Inspect the component lifecycle and network activity. |
Debugging Checklist
- Check whether the application is running in development mode.
- Check whether the component is inside
<StrictMode>. - Log both the Effect setup and its cleanup.
- Inspect the dependency array.
- Check whether a dependency is a new object or function on every render.
- Look for state updates that repeatedly trigger the Effect.
- Check whether the component is genuinely unmounting and mounting again.
- Verify that event listeners, timers, subscriptions, and requests are handled correctly.
8. When to Use It
You do not intentionally use duplicate Effect execution as a feature. However, understanding this behavior is useful when implementing and debugging:
- API integration: Understanding why development tools show duplicate network requests.
- Event subscriptions: Ensuring that listeners are registered and removed correctly.
- WebSocket connections: Ensuring that connections are cleaned up and recreated safely.
- Timers: Preventing multiple active intervals or timeouts.
- Component lifecycle debugging: Identifying whether an Effect is re-running because of Strict Mode, dependency changes, or remounting.
- Production readiness: Making synchronization logic reliable when components start and stop.
Best practice: Design Effects so that running setup, cleanup, and setup again produces the correct result.
9. When NOT to Use It
Do not use an Effect when it is unnecessary for the task.
- Do not use an Effect just to calculate derived values: Calculate values from props and state during rendering when practical.
- Do not use an Effect for a button click action: Put user-triggered operations in the event handler.
- Do not suppress repeated execution with a ref without understanding the lifecycle: This can hide missing cleanup or incorrect synchronization.
- Do not disable Strict Mode simply because an Effect runs twice: Fix the underlying Effect behavior instead.
- Do not assume the empty dependency array guarantees one execution in development: Strict Mode may perform an additional setup and cleanup cycle.
- Do not assume all repeated executions are harmless: Duplicate non-idempotent operations can cause incorrect data or side effects.
When Should You Use an Event Handler Instead?
Consider a payment button.
Incorrect design: Initiating the payment automatically in an Effect just because the component rendered.
Better design: Start the payment in the appropriate event handler when the user submits the payment request, and enforce duplicate-request protection on the server when necessary.
function PaymentButton() {
async function handlePayment() {
// Initiate the payment request.
}
return (
<button onClick={handlePayment}>
Pay Now
</button>
);
}
This makes the payment action respond to a user event rather than an Effect lifecycle. Real payment implementations also need appropriate error handling and server-side duplicate-request protection.
10. Interview Questions
Q1. Why does useEffect run twice in React?
Answer: In development, React Strict Mode performs an additional Effect setup and cleanup cycle on initial mounting to help identify missing cleanup and other lifecycle-related bugs.
Q2. Does useEffect run twice in production?
Answer: React does not perform the additional Strict Mode Effect setup and cleanup cycle in production. However, an Effect can still run more than once in production because of dependency changes, actual remounting, or other application behavior.
Q3. What is React Strict Mode?
Answer: Strict Mode is a development tool that enables additional checks to help identify potential problems in React components. It can expose missing Effect cleanup and unsafe assumptions about component lifecycle behavior.
Q4. What happens when useEffect has an empty dependency array?
Answer: The Effect is scheduled after the initial mount and does not re-run merely because ordinary component renders occur. In development Strict Mode, React may perform an additional setup and cleanup cycle.
Q5. What happens when useEffect has no dependency array?
Answer: The Effect runs after every committed render. If the Effect updates state on every execution, it can create an infinite rendering loop.
Q6. What is the purpose of the cleanup function in useEffect?
Answer: Cleanup releases resources or reverses the previous setup. Examples include removing event listeners, clearing timers, disconnecting subscriptions, and aborting requests where appropriate.
Q7. Why is cleanup important when Strict Mode is enabled?
Answer: Strict Mode checks whether an Effect can safely start, stop, and start again. Correct cleanup prevents resources from accumulating or remaining active after the previous setup has been cleaned up.
Q8. Can useEffect cause an infinite rendering loop?
Answer: Yes. If an Effect updates state and that update causes the Effect to run again without a stopping condition, React can enter an infinite update loop.
Q9. How can you prevent duplicate event listeners?
Answer: Register the listener in the Effect setup and remove the same listener in the cleanup function.
Q10. Should you disable Strict Mode to prevent duplicate API requests?
Answer: Generally, no. Investigate why the request is duplicated and use appropriate cleanup, cancellation, or data-fetching and caching mechanisms. For operations that modify data, also consider server-side protection against duplicate requests.
Q11. Why can an object or function dependency cause an Effect to run repeatedly?
Answer: Objects and functions created during rendering usually have new references on subsequent renders. React compares dependencies using Object.is, so a changed reference can cause the Effect to re-run even if the contents appear equivalent.
Q12. How do you investigate whether Strict Mode is responsible?
Answer: Check whether the application runs in development and whether the component is inside Strict Mode. Log the Effect setup and cleanup, inspect the dependency array, and check whether the component is mounting more than once for other reasons.
Q13. Should you use a ref to make an Effect run only once?
Answer: Not as a general fix. A ref-based guard can suppress a repeated setup without cleaning up resources correctly. Effects should be designed to support the component lifecycle rather than relying on a guard to skip execution.
Q14. What is the difference between an Effect re-running and a component re-rendering?
Answer: A component re-render recalculates its UI. An Effect re-runs when React determines that the Effect needs to synchronize again, such as after mounting or when one of its dependencies changes. A component can re-render without its Effect re-running if its dependencies have not changed.
Q15. What is the best practice for writing useEffect?
Answer: Use Effects only for synchronization with external systems, declare the correct dependencies, implement cleanup when required, and make setup safe to repeat.
Summary
useEffectmay run twice during initial mounting in development Strict Mode.- React performs an additional setup and cleanup cycle to help detect lifecycle-related bugs.
- The additional Strict Mode cycle does not occur in production.
- Effects can also run again because dependencies change or components remount.
- Always clean up subscriptions, timers, and other resources when necessary.
- Investigate duplicate API requests instead of automatically disabling Strict Mode.
- Use event handlers for user-triggered actions and Effects for synchronization with external systems.
| Too Many Re-renders in React :👈 | 👉:Fix Maximum Update Depth Exceeded |