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:
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →- Opening and closing a connection to a chat server or WebSocket for a given room.
- Starting and clearing a
setIntervaltimer. - Subscribing to a browser event such as
resizeonwindow. - 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.
#1 Best Overall
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.
- 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.
- 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. - 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.
- Setup runs again with the new values. The replacement setup starts the work for the current props and state.
- 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.
Recommended Free Tools
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.
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.
Rank #3
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.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →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.”
Rank #4
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.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:
- 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.
Best Value
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.
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.
Quick Recap
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.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errors




