new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

React · Learn · all subjects

escape-hatches/effects

129 notes in this subject, read out of this brain and free to use. This is page 2 of 3.

Effects fire twice in development with Strict Mode

React intentionally remounts components once in development when Strict Mode is enabled to find bugs like missing cleanup. This causes Effects to run twice: setup, cleanup, setup. This is development-only behavior. In production, Effects run only once.

Why Effects fire twice in development

React remounts components to verify that Effects work correctly after remounting. This exposes bugs where cleanup functions are missing. The correct fix is to implement the cleanup function, not to prevent the Effect from running twice.

Do not use refs to prevent Effects from firing

Using a ref to prevent an Effect from running more than once is a pitfall that does not fix the underlying bug. Even if you see the Effect run only once in development, the bug still exists for remounting scenarios. Implement proper cleanup instead.

Effects must declare all dependencies explicitly

All values used inside an Effect that change over time must be included in the dependency array. React's linter enforces this. You cannot 'choose' your dependencies—you will get a lint error if dependencies don't match what the code needs. If you don't want code to re-run, edit the Effect code itself to not need that dependency.

Ref objects have stable identity

Objects returned by useRef have stable identity—React guarantees you always get the same object from the same useRef call on every render. They never change, so refs do not need to be included in dependency arrays and will never cause Effects to re-run by themselves.

useState setter functions have stable identity

Setter functions returned by useState have stable identity, similar to refs. They can usually be omitted from dependency arrays since the linter can verify they won't change.

Effect example: controlling non-React widgets

When synchronizing with non-React widgets, ensure the Effect applies the React state to the widget. Calling a widget method twice with the same value (like setZoomLevel) is harmless and does not require cleanup. Some APIs like showModal throw on duplicate calls and need cleanup functions.

Effect example: subscribing to events cleanup

When subscribing to events in an Effect, the cleanup function must remove the event listener. Example: useEffect(() => { window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, []);

Effect example: animating with cleanup

When animating in an Effect, the cleanup function should reset the animation to initial values. For example, if the Effect sets opacity to 1, cleanup should set it back to 0. This ensures animations reset correctly on remount.

Effect example: fetching data with cleanup

When fetching data in an Effect, use a boolean flag or AbortController to ignore responses after the Effect cleanup runs. Example: let ignore = false; fetch().then(result => { if (!ignore) setState(result); }); return () => { ignore = true; };

Effect example: analytics logging

Analytics should log page visits caused by rendering. It's fine if analytics logs twice in development—don't add boilerplate to prevent it. Analytics code should not affect metrics from development machines. Logging from route change handlers is more precise than Effects.

Application initialization not in Effects

Logic that should run once when the application starts (like checking auth tokens) should be placed outside components, not in Effects. Check if running in browser with: if (typeof window !== 'undefined') { /* init code */ }

Event handlers vs Effects for state-changing actions

Actions like buying a product should be triggered by event handlers (click, submit), not Effects. Effects are for side effects caused by rendering. If remounting breaks the logic, move the code to an event handler. This prevents unintended side effects when navigating.

Each render has its own Effect closure

Each render creates its own Effect with its own closure over the state and prop values from that render. An Effect from a render with state='a' will always see 'a', even if the state changed later. This is why Effects from different renders are isolated.

Effect dependency array behaviors

There are three patterns: no dependency array means Effect runs after every render; empty [] means Effect runs only on mount; [a, b] means Effect runs on mount and when a or b change. These behaviors differ fundamentally and determine when cleanup runs.

Effects should be used sparingly

Do not rush to add Effects. Keep in mind that Effects are typically used to synchronize with external systems. If an Effect only adjusts state based on other state, you might not need an Effect.

useEffect video player example

Example: import { useState, useRef, useEffect } from 'react'; function VideoPlayer({ src, isPlaying }) { const ref = useRef(null); useEffect(() => { if (isPlaying) { ref.current.play(); } else { ref.current.pause(); } }, [isPlaying]); return <video ref={ref} src={src} loop playsInline />; } This synchronizes the video element with React state using refs and Effects.

useEffect chat room example with cleanup

Example: useEffect(() => { const connection = createConnection(); connection.connect(); return () => connection.disconnect(); }, []); This connects on mount and disconnects on unmount. The cleanup prevents connection leaks when components unmount and remount.

useEffect focus example on mount

Example: useEffect(() => { ref.current.focus(); }, []); This focuses an input element only once when it mounts. Empty dependency array ensures focus happens only on mount, not on every render.

useEffect conditional focus example

Example: useEffect(() => { if (shouldFocus) { ref.current.focus(); } }, [shouldFocus]); This focuses conditionally based on a prop. The shouldFocus dependency ensures the effect re-runs when the prop changes.

useEffect interval example with cleanup

Example: const intervalId = setInterval(onTick, 1000); return () => clearInterval(intervalId); This sets up an interval on mount and clears it on unmount or re-run. Cleanup prevents multiple intervals from accumulating.

useEffect setTimeout example with cleanup

Example: const timeoutId = setTimeout(onTimeout, 3000); return () => clearTimeout(timeoutId); This schedules work and cancels pending timeouts on cleanup. Prevents scheduled work from running after component unmounts.

Race condition protection with ignore flag in Effects

Each render's Effect has its own ignore variable, initially set to false. When an Effect gets cleaned up (such as when a dependency changes), its ignore variable becomes true. This prevents outdated asynchronous results from updating state. When a new Effect runs for the latest data, it has ignore set to false. Past Effects have their ignore flag set to true, so the if (!ignore) check prevents them from calling setState. This is more reliable than AbortController alone because more asynchronous steps could be chained after the fetch.

Race condition example: fetching bio for selected person

When selecting 'Bob', it triggers fetchBio('Bob'). When selecting 'Taylor', it triggers fetchBio('Taylor') and cleans up the previous (Bob's) Effect. If fetching 'Taylor' completes before fetching 'Bob', the Effect from the 'Taylor' render calls setBio('This is Taylor's bio'). When fetching 'Bob' completes later, the Effect from the 'Bob' render does not do anything because its ignore flag was set to true.

AbortController not sufficient alone for race conditions

While AbortController can cancel requests that are no longer needed, it is not enough by itself to protect against race conditions. More asynchronous steps could be chained after the fetch, so using an explicit flag like ignore is the most reliable way to fix this type of problem.

useMemo type inference in TypeScript

The result of calling useMemo is inferred from the return value of the callback function. You can be more explicit by providing a type argument to the Hook if needed.

POST request on component mount should be in Effect

Analytics or other POST requests that should run once when a component displays should be in an Effect with an empty dependency array. These run because the component was displayed, not because of a specific user action.

Distinguish between code that runs because of component display vs user action

When deciding whether code should be in an Effect or event handler, ask: does this code need to run because the component was displayed to the user, or because the user performed a specific action? Use Effects only for code that should run because the component was displayed.

Measure calculation expense with console.time

To determine if a calculation is expensive, add console.time() before the calculation and console.timeEnd() after. Perform the measured interaction and check the console logs. If the overall logged time adds up to 1ms or more, memoization may make sense. Test performance with artificial slowdown using tools like Chrome's CPU Throttling, and measure in production builds for accurate results.

useMemo runs during rendering only

The function wrapped in useMemo runs during rendering, so useMemo only works for pure calculations.

Avoid chains of Effects that adjust state

Do not chain Effects where each Effect adjusts state to trigger the next Effect. This causes unnecessary re-renders and makes code fragile and difficult to maintain. Instead, calculate what you can during rendering and adjust state in event handlers.

Use Effects to synchronize with external systems

Effects should be used to synchronize components with external systems like jQuery widgets or browser APIs. You can also fetch data with Effects. Modern frameworks provide more efficient built-in data fetching mechanisms than writing Effects directly in components.

Don't use Effects to handle user events

Handle user events in their corresponding event handlers, not in Effects. By the time an Effect runs, you don't know what the user did. In the event handler, you know exactly what happened. This is more efficient and predictable.

Don't use Effects to transform data for rendering

You should not write an Effect that updates state when data changes for rendering purposes. Instead, transform all data at the top level of components during rendering. This code automatically re-runs when props or state change, avoiding unnecessary render passes and cascading updates.

Effects are an escape hatch from React paradigm

Effects let you step outside of React and synchronize components with external systems like non-React widgets, networks, or browser DOM. If no external system is involved, you should not need an Effect.

Use useMemo to cache expensive calculations

Cache expensive calculations by wrapping them in a useMemo Hook. useMemo only re-runs the inner function if the dependencies have changed. This tells React not to recalculate unless specific dependencies change. React Compiler can automatically memoize expensive calculations in many cases, eliminating the need for manual useMemo.

Distinguish between state changes and user events as Effect triggers

Not all state changes should trigger Effects. Consider whether you want to run code because of a state change itself, or because the user performed a specific action. If a user action like form submission is the real cause, handle it in the event handler instead of using an Effect on the state change.

Example: Form submission without unnecessary Effect

This example shows a form where sendMessage is called directly in the handleSubmit event handler, not in an Effect. The handleSubmit function calls e.preventDefault(), updates showForm state to false, and calls sendMessage(message). This ensures the message is only sent when the user actually submits the form, not when the showForm state changes for other reasons. ```js import { useState, useEffect } from 'react'; export default function Form() { const [showForm, setShowForm] = useState(true); const [message, setMessage] = useState(''); function handleSubmit(e) { e.preventDefault(); setShowForm(false); sendMessage(message); } if (!showForm) { return ( <> <h1>Thanks for using our services!</h1> <button onClick={() => { setMessage(''); setShowForm(true); }}> Open chat </button> </> ); } return ( <form onSubmit={handleSubmit}> <textarea placeholder="Message" value={message} onChange={e => setMessage(e.target.value)} /> <button type="submit" disabled={message === ''}> Send </button> </form> ); } function sendMessage(message) { console.log('Sending message: ' + message); } ```

Form submission logic should go in event handlers, not Effects

When you want to perform an action because of a user event like form submission, put the logic in the event handler (such as handleSubmit) rather than in an Effect. This ensures the action only happens in response to the specific user action, not when related state changes for other reasons.

Summary: remove unnecessary Effects

Do not use Effects to transform data for rendering, to handle user events, or to keep two state variables synchronized. Calculate during rendering or lift state up instead. Data fetching with Effects needs cleanup for race conditions. Modern frameworks provide better data fetching solutions.

useSyncExternalStore Hook signature

useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) - subscribe is a function that subscribes and returns an unsubscribe function, getSnapshot returns the current value on client, getServerSnapshot returns the value on server for SSR.

Use useSyncExternalStore for external store subscriptions

To subscribe to external data stores, use the useSyncExternalStore Hook instead of manually subscribing in an Effect. This Hook is purpose-built for subscribing to external stores and is less error-prone than using Effects. It takes a subscribe function, a function to get the value on the client, and a function to get the value on the server.

Module-level initialization runs once per app load

Code at the top level of a module runs once when the component is imported, even if it doesn't get rendered. This can be used for app-wide initialization. However, avoid overusing this pattern. Keep app-wide initialization logic to root component modules like App.js or the application's entry point.

Initialization logic should not be in Effects in top-level component

Avoid placing initialization logic that should run once per app load in an Effect in the top-level component. Effects run twice in development and the component may be remounted in practice. Instead, add a top-level variable to track if initialization already executed, or run initialization during module initialization before the app renders.

Efficiency problem with chained Effects

Chains of Effects cause multiple re-renders between each state update. In the worst case, each Effect triggers a state update that triggers the next Effect, causing cascading re-renders.

Call pure functions outside Effects to extract primitive values

If the parent passes a pure function that returns an object, call it outside the Effect to extract primitive values. Then use those primitive values as dependencies. This prevents the Effect from re-synchronizing on every parent render just because the function reference changed.

Extract primitive values from object props

When receiving an object as a prop, destructure and extract primitive values from it outside the Effect. Then use those primitive values as dependencies instead of the object. This avoids unnecessary Effect reruns when the parent component recreates the object with the same content.

Move dynamic objects and functions inside the Effect

If an object or function depends on reactive values like props, don't declare it outside the component. Instead, create it inside the Effect. Then depend only on the primitive values extracted from the object or function, not the object or function itself.

Move static objects and functions outside the component

If an object or function doesn't depend on any props or state, declare it outside the component. This proves to the linter it's not reactive and won't change on re-renders, so it doesn't need to be a dependency. This prevents Effects from unnecessarily re-synchronizing.

Objects and functions are considered different if created at different times

In JavaScript, each newly created object or function is considered distinct from all others, even if their contents are identical. Two objects with identical properties are not equal: {a: 1} !== {a: 1}. This means objects and functions created during renders can cause Effects to retrigger unintentionally if used as dependencies.

Wrap event handler props in Effect Events to avoid unnecessary reruns

When a component receives an event handler as a prop that changes on every render, wrap it in an Effect Event and call the Effect Event from your Effect instead of the prop directly. This prevents the Effect from re-synchronizing every time the parent re-renders with a new function reference.

Use Effect Events to read values without reacting to changes

Effect Events (useEffectEvent) let you extract non-reactive logic from an Effect. Inside an Effect Event, you can read the latest value of a prop or state without making it a dependency. The Effect Event itself is not reactive, so changes to values it reads won't retrigger the Effect.

Use updater functions to avoid state dependencies

Instead of reading state inside an Effect and making it a dependency, pass an updater function to setState. For example, use setMessages(msgs => [...msgs, newMessage]) instead of setMessages([...messages, newMessage]). This lets React apply the update based on the latest state without the Effect needing to depend on that state.

Split Effects that synchronize unrelated things

If an Effect synchronizes two independent processes, split it into separate Effects, each with its own dependency list. For example, if one Effect fetches cities based on country and another fetches areas based on city, they should be two separate Effects. This prevents one dependency change from unnecessarily retriggering the other process.

Move event-specific logic to event handlers, not Effects

If code should run in response to a specific user interaction (like form submission), put that logic directly in an event handler, not in an Effect. Effects are for synchronizing with external systems. For example, sending a POST request and showing a notification on form submit should be in the submit handler, not in an Effect that depends on a 'submitted' state.

Three-step workflow for fixing Effect dependencies

First, change the code of your Effect or how your reactive values are declared. Second, follow the linter and adjust the dependencies to match the changed code. Third, if you're not happy with the dependency list, go back to step one and change the code again. Don't suppress the linter; change the code instead.

Suppressing the dependency linter is dangerous

Using eslint-disable-next-line or similar to suppress the react-hooks/exhaustive-deps linter leads to unintuitive bugs that are hard to find and fix. When dependencies don't match the code, you create a high risk of introducing bugs because you 'lie' to React about what your Effect depends on. It is better to treat linter errors as compilation errors and fix the underlying code.

To remove a dependency, prove it is not reactive

You cannot arbitrarily choose to remove dependencies. If a value is reactive (can change on re-renders), it must be a dependency. To remove a dependency, you must change the code so the value is no longer reactive—for example, by moving it outside the component so it cannot change.

Reactive values include props and component body variables

Reactive values are props and all variables and functions declared directly inside the component body. These can change due to re-renders. Since reactive values can change over time, any reactive value read by an Effect must be declared in its dependency list.

Effect dependencies must match the code that uses reactive values

When you write an Effect, the linter verifies that every reactive value (like props and state) that the Effect reads is included in the dependency list. This ensures the Effect stays synchronized with the latest props and state. If you leave dependencies empty or incomplete, it can introduce bugs.

Give your agent this brain