useEffect
All about useEffect hook in React
The most misconcepted hook in React. Even senior developers make mistakes by using this effect. But let's understand why.
History
Before React v16.8 (2019), components were class components. They had something called "componentDidMount", "componentDidUpdate", and "componentWillUnMount" where you could run some code depending on the component life cycle.
The API changed completely in React v16.8, when hooks where introduced, and useEffect was introduced to replace the 3 prior API from class components.
useEffect(() => {
// componentDidMount?
}, [])
useEffect(() => {
// componentDidUpdate?
}, [something, another])
useEffect(() => {
return () => {
// componentWillUnMount?
}
}, [])
What is useEffect?
You may intuitely assume useEffect
is a lifecycle hook, where you can run code depending on the life cycle of the component, but is NOT.
While you could use useEffect
that way, that's not its intended purpose. As Dan Abramov—mantainer of React at that time—explained:
"The mental model is synchronization. Not lifecycle".
useEfffect
is meant to synchronize between your component and external source (e.g sync API response data with react).
When using useEffect, instead of asking "When does this effect run?", ask: "With which state or prop does this effect synchronize with?"
// with which state does this effect synchronize with?
useEffect(fn) // all state
useEffect(fn, [l) // no state
useEffect(fn, [these, states])
For example, let's synchronize a websocket that listen to a subscription and when the itemId
changes, it will synchronize to another subscription. Of course, when the component unmounts it will unsubscribe.
useEffect(() => {
// Synchronize with external system
const sub = storeApi.subscribeToItem(itemId, setItemData) ;
// Subscription disposal
return sub.unsubscribe;
}, [itemid]); // Subscription dependency
The mental model is synchronization.
Do not use useEffect
:
- to run code that derives from local source (props/state/conditions). Most of the times this code belongs outisde of useEffect or directly inside
event handlers
functions likeonSubmit
,onChange
, or anyhandleFunction
. - to run Action effects
- to transform data
- to fetch data. I know this is controversial but must use already built-in solutions like Tanstack Query or a framework.
Do use useEffect:
- to synchronize with external systems (APIs, subscriptions, DOM APIs, timers, local storage, web sockets).
- to perform imperative DOM work that cannot be done during render (focus, measure, integrate third-party libs).
useEffect(() => {
inputRef.current?.focus();
}, []); // runs after first paint
- to run effects. What kind of effects? Short answer: Synchronized effects. There are two types of effects:
- Action effects: Don't care about the result, such as console log, send anaylitics, async functions without waiting a response, code inside event handlers (side-effects), navigation, etc.
- Synchronized/Activity effects: events that communicate with external systems and are long-lived.
- to initialize/clean-up resources tied to the component lifecycle (add/remove event listeners, set/clear timers, open/close connections).
Usage
useEffect(() => {
// some code
}, []) // dependencies
Parameters
Name | Type | Description |
---|---|---|
effect | () => void or () => () => void | The effect logic to run after the component renders. Can optionally return a cleanup function, which runs before the effect re-runs or when the component unmounts. |
deps (optional) | any[] | Array of dependencies that determine when the effect runs. • Omit to run after every render (pitfall). • Pass [] to run once after the first render.• Pass [a, b] to run when either a or b changes. |
Returns
Type | Description |
---|---|
void | useEffect itself returns nothing. |