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 · API reference · all subjects

eslint-plugin-react-hooks

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

Array.sort() mutates the original array

The sort() method mutates the array in place. Calling setItems(items.sort()) will not trigger a re-render because the array reference is unchanged. Create a new sorted array instead: setItems([...items].sort()).

Add items to array state with spread syntax

To add items to array state, use spread syntax to create a new array: setTodos([...todos, {id, text}]). Alternatively, use the functional setState form: setTodos(todos => [...todos, {id, text}]).

Update nested object state by spreading at each level

When updating nested objects, spread at each level that needs modification to create new objects at every level. Example: setUser({...user, settings: {...user.settings, theme: 'dark'}}) ensures React detects the change.

Never mutate array state with push()

Using Array.push() to add items to state will mutate the array in place. Since the array reference remains the same, React will not trigger a re-render. Instead, create a new array using spread syntax: setItems([...items, newValue]).

Invalid: cache manipulation during render

Modifying a global cache object during component render is invalid. Example: const cache = {}; function Component({id}) { if (!cache[id]) { cache[id] = fetchData(id); } return <div>{cache[id]}</div>; }

Why global mutations during render are problematic

Modifying global variables during render breaks React's purity assumption for rendering. This causes components to behave differently in development versus production, breaks Fast Refresh, and prevents optimizations like React Compiler from working correctly.

Invalid: modifying window properties during render

Assigning to window properties during component render is invalid. Example: function Component({userId}) { window.currentUser = userId; return <div>User: {userId}</div>; }

Invalid: mutating global counter in render

Incrementing a global variable during component render is invalid. Example: let renderCount = 0; function Component() { renderCount++; return <div>Count: {renderCount}</div>; }

globals rule validates against global mutations during render

The globals rule from eslint-plugin-react-hooks validates against assignment and mutation of global variables during the render phase. It ensures that side effects must run outside of render, preventing components from breaking React's assumption that rendering is pure.

Invalid: mutating global array during render

Pushing to a global array during component render is invalid. Example: const events = []; function Component({event}) { events.push(event); return <div>Events: {events.length}</div>; }

Valid: use useEffect to synchronize external state

Use useEffect hook to synchronize external state like the DOM with React state. Example: function Component({title}) { useEffect(() => { document.title = title; }, [title]); return <div>Page: {title}</div>; }

Valid: use useContext for global values

Use useContext hook to access global values instead of reading global variables. Example: function Component() { const user = useContext(UserContext); return <div>User: {user.id}</div>; }

Valid: use useState for counters

Use useState hook instead of global variables for managing counters. Example: function Component() { const [clickCount, setClickCount] = useState(0); const handleClick = () => { setClickCount(c => c + 1); }; return <button onClick={handleClick}>Clicked: {clickCount} times</button>; }

incompatible-library: react-hook-form useWatch is the compatible alternative

The useWatch hook from react-hook-form is compatible with memoization. It takes control and name parameters and returns a value that properly updates when the field changes, making it safe to use with useMemo or React Compiler.

incompatible-library rule purpose

The incompatible-library ESLint rule validates against usage of libraries that are incompatible with memoization (manual or automatic). These libraries were designed before React's memoization rules were fully documented and use patterns that aren't supported by React. When detected, the linter flags these APIs, and React Compiler automatically skips over components that use them to avoid breaking the app.

Interior mutability breaks memoization

Interior mutability is when an object or function keeps its own hidden state that changes over time, even though the reference to it stays the same. This breaks memoization because React only checks if you gave it a different object or function reference, not what's inside. When an API exhibits interior mutability, both manual useMemo and React Compiler's automatic memoization will break.

incompatible-library: react-hook-form watch is incompatible

The react-hook-form watch function exhibits interior mutability and is incompatible with memoization. Using watch('field') with useMemo will cause values to never update. The alternative is to use useWatch with control and name parameters instead.

incompatible-library: TanStack Table useReactTable is incompatible

The TanStack Table useReactTable hook returns a table instance that uses interior mutability and is incompatible with memoization.

incompatible-library: MobX observer breaks memoization

MobX patterns like the observer function break memoization assumptions. The linter does not yet detect MobX usage. If your app doesn't work with React Compiler when using MobX, you may need to use the 'use no memo' directive.

Designing React APIs for memoization compatibility

When designing a library API or hook, ensure that calling the API can be safely memoized with useMemo. If it can't be memoized safely, both manual and React Compiler memoizations will break. Design APIs that return immutable state and use explicit update functions instead of interior mutability patterns.

How memoization breaks with incompatible libraries example

When using incompatible libraries with memoization, values appear to freeze and never update. For example, with react-hook-form's watch: const name = useMemo(() => watch('name'), [watch]); will result in the name value never updating even when the field changes, causing the UI to appear frozen.

preserve-manual-memoization valid example: removing unnecessary memoization with React Compiler

Example of correct code: function Component({ items, sortBy }) { const sorted = [...items].sort((a, b) => { return a[sortBy] - b[sortBy]; }); return <List items={sorted} />; } - When using React Compiler, manual memoization is not needed and can be removed.

preserve-manual-memoization rule validates compiler memoization inference

The preserve-manual-memoization ESLint rule validates that existing manual memoization using useMemo, useCallback, and React.memo is preserved by the React Compiler. The compiler will only compile components and hooks if its inference matches or exceeds the existing manual memoization.

preserve-manual-memoization: incomplete dependency lists are invalid

The preserve-manual-memoization rule flags useMemo and useCallback calls with incomplete dependency arrays as invalid. Incomplete dependencies prevent the compiler from understanding the code's data flow and applying further optimizations. All variables used in the memoized callback or computation must be included in the dependency array.

preserve-manual-memoization: compiler preserves all manual memoization

React Compiler preserves existing useMemo, useCallback, and React.memo calls. If you have manually memoized something, the compiler assumes you had a good reason and will not remove it.

Manual memoization can be removed when using React Compiler

When using React Compiler, you can safely remove manual memoization with useMemo and useCallback and let the compiler handle optimization instead. The compiler will automatically optimize the code without explicit memoization.

preserve-manual-memoization invalid example: useMemo with missing filter dependency

Example of incorrect code: function Component({ data, filter }) { const filtered = useMemo(() => data.filter(filter), [data]); return <List items={filtered} />; } - Missing 'filter' in the dependency array.

preserve-manual-memoization invalid example: useCallback with missing value dependency

Example of incorrect code: function Component({ onUpdate, value }) { const handleClick = useCallback(() => { onUpdate(value); }, [onUpdate]); return <button onClick={handleClick}>Update</button>; } - Missing 'value' in the dependency array.

preserve-manual-memoization valid example: useMemo with complete dependencies

Example of correct code: function Component({ data, filter }) { const filtered = useMemo(() => data.filter(filter), [data, filter]); return <List items={filtered} />; } - All dependencies are included in the dependency array.

purity example: stable IDs from initial state is valid

This code is valid because randomUUID() is called only once during state initialization, not on every render: function Component() { const [id] = useState(() => crypto.randomUUID()); return <div key={id}>Content</div>; }

purity rule validates components are pure functions

The purity ESLint rule validates that React components and hooks are pure by checking that they do not call known-impure functions. React components must be pure functions - given the same props, they should always return the same JSX.

impure functions that violate purity rule

The following functions violate the purity rule because they return different values for the same inputs: Math.random(), Date.now(), new Date(), crypto.randomUUID(), and performance.now().

consequences of impure functions in render

When components use functions like Math.random() or Date.now() during render, they produce different output each time, which breaks React's assumptions and causes bugs like hydration mismatches, incorrect memoization, and unpredictable behavior.

purity example: Math.random() in render is invalid

This code is invalid because Math.random() produces different output every render: function Component() { const id = Math.random(); // Different every render return <div key={id}>Content</div>; }

purity example: Date.now() in render is invalid

This code is invalid because Date.now() changes every render: function Component() { const timestamp = Date.now(); // Changes every render return <div>Created at: {timestamp}</div>; }

correct pattern for showing current time

To show the current time, initialize the time in useState and update it in a useEffect with an interval. Do not call Date.now() during render: function Clock() { const [time, setTime] = useState(() => Date.now()); useEffect(() => { const interval = setInterval(() => { setTime(Date.now()); }, 1000); return () => clearInterval(interval); }, []); return <div>Current time: {time}</div>; }

Performance issue from synchronous setState in effects

Synchronous setState calls in effects trigger immediate re-renders before the browser can paint, causing performance issues and visual jank.

Synchronous setState in effect with derived data

Setting state immediately inside an effect that just copies props or derives values from props forces an extra render pass. Instead, calculate derived values during rendering. When something can be calculated from the existing props or state, don't put it in state.

setState in effects is acceptable with ref values

Calling setState in an effect is acceptable if the value comes from a ref, such as reading the height from getBoundingClientRect(). useLayoutEffect is appropriate for this pattern since it runs after DOM mutations but before the browser paints.

Invalid pattern: synchronous setState copying props

Synchronous setState in effect that copies props is invalid. Example: useEffect(() => { setItems(data); }, [data]) when data comes from props. Instead, initialize state with the prop value or calculate during render.

Invalid pattern: setting loading state synchronously in effect

Setting loading state synchronously in an effect is invalid. Example: useEffect(() => { setLoading(true); fetchData().then(() => setLoading(false)); }). This causes an extra render on mount before the fetch completes.

Invalid pattern: transforming data in effect

Transforming data in an effect and storing the result in state is invalid. Example: useEffect(() => { setProcessed(rawData.map(transform)); }). Instead, derive the transformed value during render.

Invalid pattern: deriving state from props in effect

Deriving state from props in an effect is invalid. Example: useEffect(() => { setSelected(items.find(i => i.id === selectedId)); }). Instead, calculate the derived value during render without storing it in state.

Valid pattern: calculate derived data during render

The correct approach to deriving data from props is to calculate it during rendering. Example: const selected = items.find(i => i.id === selectedId); return <div>{selected?.name}</div>;. This avoids an extra render cycle.

Valid pattern: setState from ref in useLayoutEffect

A valid pattern is to use setState in useLayoutEffect when reading values from refs. Example: useLayoutEffect(() => { const { height } = ref.current.getBoundingClientRect(); setTooltipHeight(height); }, []);. This is acceptable because the value comes from DOM measurement, not props or existing state.

set-state-in-effect rule overview

The set-state-in-effect ESLint rule validates against calling setState synchronously in an effect, which can lead to re-renders that degrade performance. Setting state immediately inside an effect forces React to restart the entire render cycle.

Synchronous setState in effects causes double rendering

When you update state synchronously in an effect, React must re-render your component, apply changes to the DOM, and then run effects again. This creates an extra render pass. React has to render twice: once to apply the state update, then again after effects run. This double rendering is wasteful when the same result could be achieved with a single render.

Transform data during render instead of in effects

Transform data at the top level of your component instead of in effects. This code will naturally re-run when props or state change without triggering additional render cycles.

Why hook order matters

React relies on the order in which hooks are called to correctly preserve state between renders. When hooks are called conditionally or in loops, React loses track of which state corresponds to which hook call, leading to bugs like state mismatches and 'Rendered fewer/more hooks than expected' errors.

rules-of-hooks ESLint rule purpose

The rules-of-hooks ESLint rule validates that components and hooks follow the Rules of Hooks, ensuring that hooks are called in the exact same order on every render to preserve state correctly.

Hooks in conditions violation

Calling hooks conditionally in if/else statements, ternary operators, or with &&/|| operators violates the Rules of Hooks because the order of hook calls becomes unreliable.

Hooks in loops violation

Calling hooks inside loops (for, while, do-while) violates the Rules of Hooks because the number and order of hook calls changes based on loop iterations.

Hooks after early returns violation

Calling hooks after early return statements violates the Rules of Hooks because they will not be called on every render.

Hooks in callbacks and event handlers violation

Calling hooks inside callbacks or event handlers violates the Rules of Hooks because these functions are not called during every render.

Hooks in async functions violation

Calling hooks inside async functions violates the Rules of Hooks because async functions execute independently from the render cycle.

Hooks in class methods violation

Calling hooks inside class methods violates the Rules of Hooks because hooks are only for functional components.

Hooks at module level violation

Calling hooks at module level, outside of any component or hook, violates the Rules of Hooks.

use hook special conditional rules

The use hook is different from other React hooks and can be called conditionally and in loops. However, it cannot be wrapped in try/catch and must be called inside a component or hook.

Conditional useEffect pattern fix

Instead of calling useEffect conditionally, call the hook unconditionally at the top level and check the condition inside the effect. For example, move `if (isLoggedIn)` inside the useEffect callback and add isLoggedIn to the dependency array.

Conditional state initialization pattern fix

Instead of calling useState conditionally, always call useState at the top level and conditionally set the initial value using a ternary operator in the argument to useState.

Give your agent this brain