October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk7 min

useEffect Made Simple: What It Does, When It Runs, and Why It Runs Twice

useEffect synchronizes a React component with something outside React. Here is how its setup, dependency array, and cleanup fit together, why Strict Mode runs it twice in development, and how to troubleshoot common problems.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

useEffect connects a React component to something outside React’s rendering: a network connection, a timer, a browser event listener, or a third-party widget. You give it a setup function that starts that work, a dependency array that lists the reactive values the setup reads, and an optional cleanup function that undoes the work. React runs the setup after a render has been committed, and it runs the cleanup before a replacement setup or when the component is removed. Once you think of an Effect as a synchronization rather than a general “run code after render” hook, most of its behavior, including the confusing parts, follows from that idea.

Start with the question: what am I synchronizing with?

React’s official useEffect reference puts the test plainly: “If you’re not trying to synchronize with some external system, you probably don’t need an Effect.” Source: React, “useEffect – React.”

As an Amazon Associate I earn from qualifying purchases.

Some common cases where an Effect is the right tool:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Opening and closing a connection to a chat server or WebSocket for a given room.
  • Starting and clearing a setInterval timer.
  • Subscribing to a browser event such as resize on window.
  • Initializing or tearing down a map, chart, or other third-party widget that manages DOM nodes itself.

Cases where an Effect is usually the wrong tool are ones where the work could be done during rendering or in an event handler: computing a derived value from props or state, or running logic because a user clicked a button. Putting those in an Effect adds an extra render pass and makes the data flow harder to follow.

The setup, dependencies, and cleanup cycle

Every Effect has three parts working together. The sequence below is what React does for a single Effect instance.

  1. Setup runs. After the component commits, React calls your setup function. It reads the current props and state it depends on and starts the external work.
  2. Dependencies are compared. On later renders, React compares each value in the dependency array with its previous value using Object.is. If any value differs, the Effect is due to rerun.
  3. Cleanup runs with the old values. Before the new setup, React calls the cleanup returned by the previous setup. That cleanup sees the values from the render that created it, so it can close the old connection.
  4. Setup runs again with the new values. The replacement setup starts the work for the current props and state.
  5. Cleanup runs on removal. When the component leaves the screen, React calls the latest cleanup and does not start a new setup.

A chat-room example shows how the parts fit together:

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

If the user switches roomId, React disconnects from the old room and connects to the new one. Nothing else in the component needs to change.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When does useEffect run?

An Effect runs after React has rendered the component and committed the result to the DOM. It does not run during server rendering, because Effects exist only on the client. That makes them a poor place to fetch data that the first HTML response should already contain.

For ordinary Effects, React generally lets the browser paint the screen before the Effect runs. That is the right default for network requests and subscriptions, because the user sees the rendered UI first. Effects triggered directly by a user interaction can have different paint timing, so do not assume every Effect is always deferred until after paint. If you need work to finish before the browser paints, see the useLayoutEffect section below.

What goes in the dependency array?

The dependency array describes the reactive values your setup reads. Those values include props, state, and any variables or functions declared inside the component body. Values declared outside the component do not need to be listed, because they never change between renders.

The three forms behave differently:

Dependency argument When the Effect runs Common mistake
Omitted After every commit of the component. Leaving it off when you meant to react only to specific changes.
[] (empty array) Setup runs once after the first commit. Cleanup runs when the component is removed. No reactive value triggers a rerun. Treating this as a simple “run once” guarantee and forgetting the development-only Strict Mode check described below.
[roomId, serverUrl] After a commit where any listed value differs from the previous render by Object.is. Cleanup with the old values runs first. Reading a prop or state inside the setup but leaving it out of the array.

Do not hide missing dependencies

If a setup reads a value and the array does not list it, the Effect can keep using a stale value. The fix is to make the array match what the setup actually reads, not to remove the value from the array to stop reruns. Suppressing the dependency linter to force a particular schedule usually produces the same stale-value bug in a less visible form.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Objects and functions created during render

An object or function declared inside the component is a new value on every render, even when its contents are identical. If you list it as a dependency, the Effect reruns on every render. The React reference recommends two steps first: simplify the Effect, or move the helper inside the Effect so it is not a dependency at all. Memoizing the object or function with useMemo or useCallback is a last resort, used when the other two approaches do not work.

When should I return a cleanup function?

Return a cleanup whenever the setup starts something that keeps running after the function returns: a connection, a subscription, a timer, a listener, or a widget instance. The cleanup should undo exactly what the setup did, using the same reference.

Setup Cleanup
connection.connect() connection.disconnect()
emitter.on("message", handler) emitter.off("message", handler)
setInterval(tick, 1000) clearInterval(id) using the returned ID
window.addEventListener("resize", onResize) window.removeEventListener("resize", onResize)
A store subscription Call the unsubscribe function the subscribe call returned.

Not every Effect needs a cleanup. Setting a document title or logging an analytics event once has nothing to undo, so the setup can simply return nothing.

For requests that can finish out of order, React’s own data-fetching example uses a flag in the cleanup. The setup starts the request, and the cleanup sets ignore to true, so a late response from an earlier dependency value cannot overwrite the newer result.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why does useEffect run twice?

In development with Strict Mode enabled, React runs an extra setup-and-cleanup cycle on mount. React’s reference states it directly: “When Strict Mode is on, React will run one extra development-only setup+cleanup cycle before the first real setup.” Source: React, “useEffect – React.”

The purpose is to test symmetry. If your cleanup correctly undoes your setup, the extra cycle is invisible: the connection opens, closes, and reopens, and the user sees the same result. If the cleanup is missing or incomplete, you will see a duplicate connection, a doubled timer, or a listener that fires twice, which is the bug Strict Mode is designed to expose.

This behavior does not happen in production. Production builds do not run the extra cycle, so do not write code that depends on the Effect running once or twice in either environment.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Fetching data inside an Effect

React’s documentation says manual fetching inside an Effect is possible, but it lists tradeoffs you should weigh before choosing it:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Effects do not run on the server, so data fetched this way arrives only after JavaScript loads and runs in the browser.
  • If a parent fetches in an Effect and then renders a child that also fetches, the requests run in sequence, creating a network waterfall.
  • Direct fetches in Effects often miss opportunities for preloading and caching.
  • Handling race conditions, such as out-of-order responses, requires boilerplate like the ignore flag above.

React recommends a framework’s data-loading mechanism where one is available, or a client-side caching library. The reference names TanStack Query, useSWR, and React Router 6.4+ as examples. A direct Effect fetch is not automatically wrong: it is a reasonable choice for simple cases in an app without those tools, provided you handle cleanup and race conditions correctly.

useLayoutEffect for work that must happen before paint

Use useLayoutEffect when a visual measurement or adjustment would otherwise flicker. Tooltip positioning is the typical case: the component measures its own DOM, then moves a tooltip. Because useLayoutEffect runs before the browser repaints, the first visible frame already shows the corrected position.

The tradeoff is that useLayoutEffect can block painting while it runs. Keep its work short, and use useEffect for everything else, including network requests and subscriptions.

Troubleshooting common symptoms

My Effect runs twice when the component mounts

Check whether Strict Mode is enabled in development. If it is, the extra setup-and-cleanup cycle is intentional. The fix is to make sure your cleanup removes everything the setup created, so the second cycle leaves nothing behind.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

My Effect runs after every render

Check first whether the dependency argument is missing entirely. If the array is present, look for an object or function declared in the component body that is listed as a dependency. Each render produces a new identity, so the Effect reruns. Move that value inside the Effect or simplify the Effect, as described above.

My Effect keeps re-running in a loop

This usually happens when the Effect sets state and that state is also one of its dependencies, so each update triggers the Effect again. Before adjusting the array, ask whether the Effect needs to exist at all. Many loops come from using an Effect to coordinate application data flow that could be handled in an event handler or during rendering.

Cleanup runs even though the component did not unmount

This is expected. A changed dependency triggers cleanup for the previous setup before the new setup begins. The component stays on screen, but the old connection or subscription is closed and replaced.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.