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

react/hooks

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

useLayoutEffect example: tooltip height measurement

import { useState, useRef, useLayoutEffect } from 'react'; function Tooltip() { const ref = useRef(null); const [tooltipHeight, setTooltipHeight] = useState(0); useLayoutEffect(() => { const { height } = ref.current.getBoundingClientRect(); setTooltipHeight(height); }, []); // ...use tooltipHeight in the rendering logic below... } This example shows measuring the tooltip's height before the browser repaints the screen, allowing the tooltip to be positioned correctly in the second render pass without flickering.

useLayoutEffect only runs on the client

Effects only run on the client. They do not run during server rendering.

useLayoutEffect vs useEffect blocking behavior

useLayoutEffect blocks the browser from repainting the screen and guarantees that the code inside it and any state updates scheduled inside it will be processed before the browser repaints. useEffect does not block the browser, allowing it to repaint before the effect runs. This means with useEffect, intermediate visual states may be visible to users, while with useLayoutEffect they are not.

useLayoutEffect for measuring layout before repaint

useLayoutEffect is used to perform layout measurements before the browser repaints the screen. This is useful for components that need to measure their size or position and adjust rendering based on that information without the user seeing intermediate states.

useMemo return value

On the initial render, useMemo returns the result of calling calculateValue with no arguments. On subsequent renders, it either returns the already stored value from the last render if dependencies haven't changed, or calls calculateValue again and returns the result.

useMemo pitfall: arrow function returning object

In useMemo(() => { key: value }, [deps]), the braces are interpreted as the function body, not an object literal, resulting in undefined being returned. To fix this, either use parentheses useMemo(() => ({ key: value }), [deps]) or use an explicit return statement useMemo(() => { return { key: value }; }, [deps]).

useMemo must be called at top level

useMemo is a Hook and can only be called at the top level of a component or custom Hook. It cannot be called inside loops or conditions. If you need conditional memoization, extract a new component and move the state into it.

useMemo dependencies array comparison

React compares each dependency with its previous value using Object.is() comparison. The dependencies array must have a constant number of items and be written inline like [dep1, dep2, dep3]. If your linter is configured for React, it will verify that every reactive value is correctly specified as a dependency.

useMemo calculateValue function requirements

The calculateValue function must be pure, must take no arguments, and should return a value of any type. During development in Strict Mode, React calls the calculation function twice to help find accidental impurities, but this is development-only behavior and does not affect production.

useMemo as performance optimization only

useMemo should only be relied upon as a performance optimization, not as a semantic guarantee. If code doesn't work without useMemo, find and fix the underlying problem first, then add useMemo to improve performance. A state variable or ref may be more appropriate in some cases.

useMemo cannot be used in loops

useMemo cannot be called inside loops or conditionally. To memoize calculations for list items, extract a separate component for each item and call useMemo at the top level of that component. Alternatively, wrap the list item component in memo() instead of using useMemo.

useMemo hook signature and basic usage

useMemo is called with two arguments: useMemo(calculateValue, dependencies). It caches the result of a calculation between re-renders. The calculateValue parameter is a function that takes no arguments and returns a value of any type. The dependencies parameter is an array of reactive values. React calls calculateValue during the initial render and on subsequent renders returns the cached value if dependencies haven't changed, otherwise calls calculateValue again.

Alternatives to useMemo for reducing unnecessary re-renders

Four principles can make memoization unnecessary: (1) Let wrapper components accept JSX as children so children don't re-render when wrapper state updates. (2) Prefer local state and don't lift state up further than necessary. (3) Keep rendering logic pure. (4) Avoid unnecessary Effects that update state, as chains of updates from Effects cause repeated re-renders.

When to use useMemo: specific cases

useMemo is valuable in these cases: (1) The calculation is noticeably slow and its dependencies rarely change. (2) The value is passed as a prop to a component wrapped in memo to skip re-rendering. (3) The value is later used as a dependency of another Hook like useEffect or another useMemo. In other cases there is no significant benefit.

useMemo cache invalidation

React will not throw away the cached value unless there is a specific reason to do so. In development, React throws away the cache when you edit the file of your component. Both in development and production, React will throw away the cache if your component suspends during the initial mount. React may add more features in the future that take advantage of throwing away the cache, such as virtualized lists.

useMemo pitfall: forgetting dependency array

If you forget to pass the dependency array as a second argument to useMemo, the calculation will re-run on every render, defeating the purpose of memoization. The correct syntax is useMemo(calculateValue, [dep1, dep2]).

useMemo strict mode double execution

In Strict Mode, React calls the calculation function twice on every re-render to help find accidental impurities. This is development-only behavior and does not affect production. If your calculation function is pure, this should not affect your logic as React ignores the result of one call.

useCallback instead of useMemo for functions

To memoize a function, use useCallback instead of useMemo. useCallback((orderDetails) => { /* ... */ }, [productId, referrer]) is equivalent to useMemo(() => (orderDetails) => { /* ... */ }, [productId, referrer]) but is more concise and readable.

useMemo with dependent Hook calculations

When one useMemo calculation depends on an object created in the component body, you should memoize that object first before passing it as a dependency to another useMemo. Alternatively, move the object declaration inside the useMemo calculation function to avoid dependency issues.

useMemo for Effect dependencies

useMemo can prevent Effects from firing too often by memoizing objects used inside Effects. For example, wrapping an options object in useMemo ensures the Effect only re-runs when the dependencies of useMemo change, not on every render. However, it's often better to move the object creation inside the Effect to avoid the need for useMemo.

useMemo with memo for skipping component re-renders

useMemo can be combined with React.memo to skip re-rendering child components. When you pass a memoized value as a prop to a component wrapped in memo, the child will skip re-rendering if the memoized value hasn't changed. Without useMemo, functions and objects created during each render will always be new, preventing memo from working effectively.

useMemo for skipping expensive recalculations example

Example: import { useMemo } from 'react'; function TodoList({ todos, tab, theme }) { const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]); // ... }. This caches the filterTodos result so it's only recalculated when todos or tab changes, not when theme changes.

React Compiler and useMemo

React Compiler automatically memoizes values and functions, reducing the need for manual useMemo calls. You can use the compiler to handle memoization automatically instead of manually calling useMemo.

useReducer signature and basic usage

useReducer is called with the signature: const [state, dispatch] = useReducer(reducer, initialArg, init?). It must be called at the top level of a component. It returns an array with exactly two values: the current state and a dispatch function.

Reducer function purity requirement in Strict Mode

In Strict Mode, React calls reducer and initializer functions twice to find accidental impurities. If your reducer mutates state or has side effects, you will see the mistakes manifested by the function being called twice. Ensure reducer functions are pure and only return new state objects.

Passing initializer function to avoid recreating initial state

To avoid recreating the initial state on every render, pass the initializer function itself (not its result) as the third argument to useReducer. For example: const [state, dispatch] = useReducer(reducer, username, createInitialState). This way, createInitialState only runs during initialization, not on every render.

useReducer with multiple state fields example

function reducer(state, action) { switch (action.type) { case 'incremented_age': { return { name: state.name, age: state.age + 1 }; } case 'changed_name': { return { name: action.nextName, age: state.age }; } } throw Error('Unknown action: ' + action.type); } const initialState = { name: 'Taylor', age: 42 }; export default function Form() { const [state, dispatch] = useReducer(reducer, initialState); function handleButtonClick() { dispatch({ type: 'incremented_age' }); } function handleInputChange(e) { dispatch({ type: 'changed_name', nextName: e.target.value }); } return ( <> <input value={state.name} onChange={handleInputChange} /> <button onClick={handleButtonClick}> Increment age </button> <p>Hello, {state.name}. You are {state.age}.</p> </> ); }

Reducer function must handle all action types or throw error

If your entire reducer state becomes undefined after dispatching, you are likely forgetting to return state in one of the cases or your action type does not match any case statement. Always include a throw statement outside the switch to catch unknown actions: throw Error('Unknown action: ' + action.type).

Missing state fields after dispatch

If a part of your reducer state becomes undefined after dispatching, you likely forgot to copy all existing fields when returning new state. Use object spread syntax: return { ...state, updatedField: newValue } to ensure all fields are preserved.

Mutating state directly prevents re-render

If you mutate an object or array in state directly and return it, React will ignore the update because the new value is identical to the previous value as determined by Object.is comparison. Always create new objects or arrays when updating state.

dispatch does not change state immediately in event handler

Calling dispatch does not change the state variable in your currently running event handler. State behaves like a snapshot. If you need to know the next state value, you can calculate it by calling the reducer function manually: const nextState = reducer(state, action).

useReducer with initializer function example

function createInitialState(username) { const initialTodos = []; for (let i = 0; i < 50; i++) { initialTodos.push({ id: i, text: username + "'s task #" + (i + 1) }); } return { draft: '', todos: initialTodos, }; } function reducer(state, action) { switch (action.type) { case 'changed_draft': { return { draft: action.nextDraft, todos: state.todos, }; }; case 'added_todo': { return { draft: '', todos: [{ id: state.todos.length, text: state.draft }, ...state.todos] } } } throw Error('Unknown action: ' + action.type); } export default function TodoList({ username }) { const [state, dispatch] = useReducer( reducer, username, createInitialState ); // ... }

Reducer function must return new object, not mutate state

State is read-only. Do not modify any objects or arrays in state directly. Instead, always return new objects from your reducer. Use object spread syntax like { ...state, age: state.age + 1 } to create new state objects.

useReducer basic example with counter

import { useReducer } from 'react'; function reducer(state, action) { if (action.type === 'incremented_age') { return { age: state.age + 1 }; } throw Error('Unknown action.'); } export default function Counter() { const [state, dispatch] = useReducer(reducer, { age: 42 }); return ( <> <button onClick={() => { dispatch({ type: 'incremented_age' }) }}> Increment age </button> <p>Hello! You are {state.age}.</p> </> ); }

useReducer cannot be called inside loops or conditions

useReducer is a Hook and can only be called at the top level of a component or in custom Hooks. You cannot call it inside loops or conditions. If you need that, extract a new component and move the state into it.

React skips re-render if new state equals previous state

If the new value returned from the reducer is identical to the current state, as determined by an Object.is comparison, React will skip re-rendering the component and its children. React may still need to call the component before ignoring the result, but it should not affect your code.

dispatch only updates state for next render

Calling the dispatch function only updates the state variable for the next render. If you read the state variable after calling dispatch, you will still get the old value that was on the screen before the call.

dispatch function parameter and return value

The dispatch function takes a single argument: the action performed by the user, which can be a value of any type. By convention, an action is usually an object with a type property identifying it and optionally other properties with additional information. The dispatch function does not have a return value.

dispatch function stable identity

The dispatch function returned by useReducer has a stable identity across renders, so it is often safe to omit it from Effect dependencies without errors. Including it in dependencies will not cause the Effect to fire.

useReducer in Strict Mode calls reducer and initializer twice

In Strict Mode, React calls the reducer and initializer functions twice during development only to help find accidental impurities. This is development-only behavior and does not affect production. The result from one of the calls is ignored.

useReducer init parameter

The optional init parameter is an initializer function that should return the initial state. If init is not specified, the initial state is set to initialArg. Otherwise, the initial state is set to the result of calling init(initialArg).

Event handler wrong syntax causes infinite re-renders

If you get 'Too many re-renders' error, you likely unconditionally dispatch an action during render, causing an infinite loop. Make sure you pass event handlers correctly: use onClick={handleClick} not onClick={handleClick()} to pass the function reference, not call it during render.

useReducer initialArg parameter

The initialArg parameter is the value from which the initial state is calculated. It can be a value of any type. How the initial state is calculated from it depends on the optional init argument.

useReducer reducer function requirements

The reducer function must be pure, should take the state and action as arguments, and should return the next state. State and action can be of any types.

useOptimistic with multiple action types example

Example of useOptimistic reducer handling multiple action types (add, remove, update_quantity): ```js const [optimisticCart, dispatch] = useOptimistic( cart, (currentCart, action) => { switch (action.type) { case 'add': const exists = currentCart.find(item => item.id === action.item.id); if (exists) { return currentCart.map(item => item.id === action.item.id ? { ...item, quantity: item.quantity + 1, pending: true } : item ); } return [...currentCart, { ...action.item, quantity: 1, pending: true }]; case 'remove': return currentCart.filter(item => item.id !== action.id); case 'update_quantity': return currentCart.map(item => item.id === action.id ? { ...item, quantity: action.quantity, pending: true } : item ); default: return currentCart; } } ); function handleAdd(item) { startTransition(async () => { dispatch({ type: 'add', item }); await cartActions.add(item); }); } ``` This shows how a reducer pattern with action objects handles multiple types of optimistic updates in a single hook.

useOptimistic with reducer for multiple values example

Example of useOptimistic with a reducer to update multiple related values together: ```js const [optimisticState, updateOptimistic] = useOptimistic( { isFollowing: user.isFollowing, followerCount: user.followerCount }, (current, isFollowing) => ({ isFollowing, followerCount: current.followerCount + (isFollowing ? 1 : -1) }) ); function handleClick() { const newFollowState = !optimisticState.isFollowing; startTransition(async () => { updateOptimistic(newFollowState); await followAction(newFollowState); }); } ``` This shows how a reducer ensures the button text and count always stay in sync when updating related values optimistically.

useOptimistic with prop/state value example

Example of useOptimistic wrapping a prop value: ```js const [isLiked, setIsLiked] = useState(false); const [optimisticIsLiked, setOptimisticIsLiked] = useOptimistic(isLiked); function handleClick() { startTransition(async () => { const newValue = !optimisticIsLiked; setOptimisticIsLiked(newValue); const updatedValue = await toggleLike(newValue); startTransition(() => { setIsLiked(updatedValue); }); }); } ``` This shows how the optimistic state immediately updates to show a toggled value while the server request completes in the background.

useOptimistic with hardcoded value example

Example of useOptimistic with a hardcoded initial value: ```js const [isPending, setIsPending] = useOptimistic(false); function handleClick() { startTransition(async () => { setIsPending(true); await action(); }); } ``` This shows how to use a hardcoded false value to track a pending state, immediately showing true while an action completes.

Handling Action failures with useOptimistic

If an Action throws an error, the Transition still ends and React renders with whatever value is currently set. Since the parent typically only updates value on success, a failure means value hasn't changed, so the UI shows what it showed before the optimistic update. You can catch the error in a try/catch block to show an error message to the user, and the optimistic state will automatically roll back to show the original state.

How final optimistic state is determined

The value argument to useOptimistic determines what displays after an Action finishes. With hardcoded values like useOptimistic(false), the state remains false after the Action, useful for pending states starting from false. With props or state passed in like useOptimistic(isLiked), if the parent updates the value during the Action, the new value is used after the Action completes, reflecting the result of the Action. With a reducer pattern like useOptimistic(items, fn), if items changes while the Action is pending, React re-runs the reducer with the new items to recalculate the state, keeping optimistic additions on top of the latest data.

Optimistic state is temporary

Optimistic state only renders while an Action is in progress. Once the Action completes, the value passed to useOptimistic is rendered instead. If the server returns a different value (e.g., 'c' instead of the optimistically set 'b'), both value and optimisticState will be rendered as 'c', not 'b'.

How optimistic state lifecycle works

When useOptimistic is used: 1) When the setter is called inside an Action, React immediately re-renders showing the optimistic state while the Action is in progress. 2) If you await in the Action, React continues showing the optimistic state. 3) A Transition is scheduled for the real state update. 4) If the real state update suspends, React continues showing the optimistic state. 5) Finally, the real state commits and optimistic state is rendered to match the new value. There is no extra render to clear the optimistic state; optimistic and real state converge in the same render when the Transition completes.

useOptimistic set function parameters

The set function returned by useOptimistic accepts either a direct value of any type, or an updater function. If you pass a function (updater function), it must be pure, take the pending state as its only argument, and return the next optimistic state. React will queue updater functions and re-render the component, applying queued updaters to the previous state similar to useState updaters. If you provided a reducer to useOptimistic, the value will be passed as the second argument to the reducer. The set function does not have a return value.

useOptimistic set function must be called inside Action

The set function returned by useOptimistic must be called inside an Action (a function called inside startTransition or inside an Action prop). If you call the setter outside an Action, React will show a warning and the optimistic state will briefly render then revert. When using Action props, you can call the set function directly without startTransition because Action props are already called inside startTransition.

useOptimistic hook signature

useOptimistic is a React Hook called at the top level of a component. It takes the signature: const [optimisticState, setOptimistic] = useOptimistic(value, reducer?). The first parameter 'value' is the value returned when there are no pending Actions. The optional second parameter 'reducer' is a function with signature reducer(currentState, action) that specifies how optimistic state gets updated; it must be pure and return the next optimistic state. The hook returns an array with two values: optimisticState (the current optimistic state, equal to 'value' unless an Action is pending) and a set function that lets you update the optimistic state to a different value inside an Action.

useOptimistic with error recovery example

Example of useOptimistic with error handling and automatic rollback: ```js const [error, setError] = useState(null); const [optimisticItems, removeItem] = useOptimistic( items, (currentItems, idToRemove) => currentItems.map(item => item.id === idToRemove ? { ...item, deleting: true } : item ) ); function handleDelete(id) { setError(null); startTransition(async () => { removeItem(id); try { await deleteAction(id); } catch (e) { setError(e.message); } }); } ``` This shows how to handle Action failures, where the UI automatically rolls back to show the item again when deletion fails.

useOptimistic in Action props without startTransition

Example of using useOptimistic inside an Action prop without needing startTransition: ```js import { useOptimistic, startTransition } from 'react'; import { updateName } from './actions.js'; export default function EditName({ name, action }) { const [optimisticName, setOptimisticName] = useOptimistic(name); async function submitAction(formData) { const newName = formData.get('name'); setOptimisticName(newName); const updatedName = await updateName(newName); startTransition(() => { action(updatedName); }) } return ( <form action={submitAction}> <p>Your name is: {optimisticName}</p> <p> <label>Change it: </label> <input type="text" name="name" disabled={name !== optimisticName} /> </p> </form> ); } ``` This shows how Action props (named with 'Action' by convention) are already called inside startTransition, so you can call the optimistic setter directly.

useOptimistic for optimistically adding to a list

Example of useOptimistic with a reducer to optimistically add items to a list: ```js const [optimisticTodos, addOptimisticTodo] = useOptimistic( todos, (currentTodos, newTodo) => [ ...currentTodos, { id: newTodo.id, text: newTodo.text, pending: true } ] ); function handleAddTodo(text) { const newTodo = { id: crypto.randomUUID(), text: text }; startTransition(async () => { addOptimisticTodo(newTodo); await addTodoAction(newTodo); }); } ``` This shows how a reducer-based approach ensures a new todo is added to the latest list even if the todos prop changes while the Action is pending.

Updater vs reducer pattern for useOptimistic

useOptimistic supports two patterns for calculating state: Updater functions pass a function to the setter (e.g., setOptimistic(current => !current)), similar to useState updaters. Reducers separate update logic from the setter call using a reducer function (e.g., dispatch({ type: 'add', item })). Use updaters for calculations where the setter call naturally describes the update. Use reducers when you need to pass data to the update (like which item to add) or when handling multiple types of updates. Reducers are essential when the base state might change while a Transition is pending, as they allow React to re-run the reducer with new state to recalculate what to show, ensuring optimistic updates work correctly with the latest data.

Checking if useOptimistic is pending

There are three ways to know when useOptimistic is pending: 1) Check if optimisticValue !== value (if not equal, a Transition is in progress). 2) Use useTransition hook which provides isPending flag (equivalent to option 1 since useTransition uses useOptimistic under the hood). 3) Add a pending flag in your reducer to show loading state for individual items.

Give your agent this brain