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 4 of 5.

useState state is read-only - replace instead of mutate

In React, state is considered read-only, so you should replace it rather than mutate your existing objects. For example, if you have a form object in state, don't mutate it like form.firstName = 'Taylor'. Instead, replace the whole object by creating a new one using spread syntax like setForm({ ...form, firstName: 'Taylor' }).

useState example - object state with spread operator

Example showing useState with an object state variable. Each input has a change handler that calls setForm with the next state of the entire form. The { ...form } spread syntax ensures that the state object is replaced rather than mutated: ```js import { useState } from 'react'; export default function Form() { const [form, setForm] = useState({ firstName: 'Barbara', lastName: 'Hepworth', email: 'bhepworth@sculpture.com', }); return ( <> <label> First name: <input value={form.firstName} onChange={e => { setForm({ ...form, firstName: e.target.value }); }} /> </label> <label> Last name: <input value={form.lastName} onChange={e => { setForm({ ...form, lastName: e.target.value }); }} /> </label> <label> Email: <input value={form.email} onChange={e => { setForm({ ...form, email: e.target.value }); }} /> </label> <p> {form.firstName} {form.lastName} ({form.email}) </p> </> ); } ```

useState example - nested object state

Example showing useState with nested object state. When you update nested state, you need to create a copy of the object you're updating, as well as any objects containing it on the way upwards: ```js import { useState } from 'react'; export default function Form() { const [person, setPerson] = useState({ name: 'Niki de Saint Phalle', artwork: { title: 'Blue Nana', city: 'Hamburg', image: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', } }); function handleNameChange(e) { setPerson({ ...person, name: e.target.value }); } function handleTitleChange(e) { setPerson({ ...person, artwork: { ...person.artwork, title: e.target.value } }); } function handleCityChange(e) { setPerson({ ...person, artwork: { ...person.artwork, city: e.target.value } }); } function handleImageChange(e) { setPerson({ ...person, artwork: { ...person.artwork, image: e.target.value } }); } return ( <> <label> Name: <input value={person.name} onChange={handleNameChange} /> </label> <label> Title: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> City: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> Image: <input value={person.artwork.image} onChange={handleImageChange} /> </label> <p> <i>{person.artwork.title}</i> {' by '} {person.name} <br /> (located in {person.artwork.city}) </p> <img src={person.artwork.image} alt={person.artwork.title} /> </> ); } ```

useState example - array state with spread and filter

Example showing useState with array state. Each button handler calls setTodos with the next version of that array. The [...todos] spread syntax, todos.map() and todos.filter() ensure the state array is replaced rather than mutated: ```js import { useState } from 'react'; import AddTodo from './AddTodo.js'; import TaskList from './TaskList.js'; let nextId = 3; const initialTodos = [ { id: 0, title: 'Buy milk', done: true }, { id: 1, title: 'Eat tacos', done: false }, { id: 2, title: 'Brew tea', done: false }, ]; export default function TaskApp() { const [todos, setTodos] = useState(initialTodos); function handleAddTodo(title) { setTodos([ ...todos, { id: nextId++, title: title, done: false } ]); } function handleChangeTodo(nextTodo) { setTodos(todos.map(t => { if (t.id === nextTodo.id) { return nextTodo; } else { return t; } })); } function handleDeleteTodo(todoId) { setTodos( todos.filter(t => t.id !== todoId) ); } return ( <> <AddTodo onAddTodo={handleAddTodo} /> <TaskList todos={todos} onChangeTodo={handleChangeTodo} onDeleteTodo={handleDeleteTodo} /> </> ); } ```

useState with Immer library for simplified immutable updates

Example showing useState with the Immer library. Immer lets you write concise code as if you were mutating objects, but under the hood it performs immutable updates: ```js import { useState } from 'react'; import { useImmer } from 'use-immer'; let nextId = 3; const initialList = [ { id: 0, title: 'Big Bellies', seen: false }, { id: 1, title: 'Lunar Landscape', seen: false }, { id: 2, title: 'Terracotta Army', seen: true }, ]; export default function BucketList() { const [list, updateList] = useImmer(initialList); function handleToggle(artworkId, nextSeen) { updateList(draft => { const artwork = draft.find(a => a.id === artworkId ); artwork.seen = nextSeen; }); } return ( <> <h1>Art Bucket List</h1> <h2>My list of art to see:</h2> <ItemList artworks={list} onToggle={handleToggle} /> </> ); } function ItemList({ artworks, onToggle }) { return ( <ul> {artworks.map(artwork => ( <li key={artwork.id}> <label> <input type="checkbox" checked={artwork.seen} onChange={e => { onToggle( artwork.id, e.target.checked ); }} /> {artwork.title} </label> </li> ))} </ul> ); } ```

useState initializer function optimization

React saves the initial state once and ignores it on the next renders. Although the result of an initializer function is only used for the initial render, the function is still called on every render if you pass the function call result instead of the function itself. To avoid this wasteful computation, pass the initializer function (not the result of calling it) to useState. For example, pass `createInitialTodos` not `createInitialTodos()`.

useState initializer function example

Example showing the difference between passing an initializer function versus calling it. This example passes the initializer function, so `createInitialTodos` only runs during initialization and not on every render: ```js import { useState } from 'react'; function createInitialTodos() { const initialTodos = []; for (let i = 0; i < 50; i++) { initialTodos.push({ id: i, text: 'Item ' + (i + 1) }); } return initialTodos; } export default function TodoList() { const [todos, setTodos] = useState(createInitialTodos); const [text, setText] = useState(''); return ( <> <input value={text} onChange={e => setText(e.target.value)} /> <button onClick={() => { setText(''); setTodos([{ id: todos.length, text: text }, ...todos]); }}>Add</button> <ul> {todos.map(item => ( <li key={item.id}> {item.text} </li> ))} </ul> </> ); } ```

useState reset component state with key

You can reset a component's state by passing a different key to a component. When the key changes, React re-creates the component (and all of its children) from scratch, so its state gets reset.

useState example - resetting state with key prop

Example showing how to reset a component's state using the key prop. The Reset button changes the version state variable, which is passed as a key to the Form. When the key changes, React re-creates the Form component from scratch: ```js import { useState } from 'react'; export default function App() { const [version, setVersion] = useState(0); function handleReset() { setVersion(version + 1); } return ( <> <button onClick={handleReset}>Reset</button> <Form key={version} /> </> ); } function Form() { const [name, setName] = useState('Taylor'); return ( <> <input value={name} onChange={e => setName(e.target.value)} /> <p>Hello, {name}.</p> </> ); } ```

useState storing information from previous renders

In rare cases you might want to adjust state in response to rendering by calling a set function during render. You can do this by comparing the previous value with the current value and updating state inside a condition. This pattern can be hard to understand and is usually best avoided, but it's better than updating state in an effect. When you call the set function during render, React will re-render that component immediately after your component exits with a return statement, and before rendering the children.

useState example - storing information from previous renders

Example showing how to track changes from the previous render. This CountLabel component shows whether the count has increased or decreased since the last change by calling a set function while rendering: ```js import { useState } from 'react'; export default function CountLabel({ count }) { const [prevCount, setPrevCount] = useState(count); const [trend, setTrend] = useState(null); if (prevCount !== count) { setPrevCount(count); setTrend(count > prevCount ? 'increasing' : 'decreasing'); } return ( <> <h1>{count}</h1> {trend && <p>The count is {trend}</p>} </> ); } ```

useState pitfall - state update does not change current value

Calling the set function does not change state in the running code. If you call the set function and then immediately read the state variable, you will still get the old value. This is because states behaves like a snapshot. Updating state requests another render with the new state value, but does not affect the state variable in your already-running code.

useState pitfall example - reading old state value

Example showing the pitfall where reading state after calling set returns the old value: ```js function handleClick() { console.log(count); // 0 setCount(count + 1); // Request a re-render with 1 console.log(count); // Still 0! setTimeout(() => { console.log(count); // Also 0! }, 5000); } ``` If you need to use the next state, you can save it in a variable before passing it to the set function: ```js const nextCount = count + 1; setCount(nextCount); console.log(count); // 0 console.log(nextCount); // 1 ```

useState pitfall - mutating state object causes no update

React will ignore your update if the next state is equal to the previous state, as determined by an Object.is comparison. This usually happens when you change an object or an array in state directly. For example, if you mutate an existing object and pass it back to setState, React will ignore the update because the reference is the same.

useState pitfall example - mutating object causes no update

Example showing the pitfall where mutating a state object causes no update: ```js obj.x = 10; // 🚩 Wrong: mutating existing object setObj(obj); // 🚩 Doesn't do anything ``` To fix this, you need to replace the object instead of mutating it: ```js // ✅ Correct: creating a new object setObj({ ...obj, x: 10 }); ```

useState pitfall example - too many re-renders error

Example showing the common pitfall that causes 'Too many re-renders' error: ```js // 🚩 Wrong: calls the handler during render return <button onClick={handleClick()}>Click me</button> // ✅ Correct: passes down the event handler return <button onClick={handleClick}>Click me</button> // ✅ Correct: passes down an inline function return <button onClick={(e) => handleClick(e)}>Click me</button> ```

useState pitfall - storing a function gets called instead

You cannot put a function into state directly by passing it as the initial state or as the next state, because React will treat it as an initializer function or updater function and call it instead of storing it. To actually store a function, you have to wrap it with another function using the arrow function syntax.

useState pitfall example - storing function gets called

Example showing the pitfall where trying to store a function gets called instead: ```js // 🚩 Wrong: someFunction gets called const [fn, setFn] = useState(someFunction); function handleClick() { setFn(someOtherFunction); // someOtherFunction gets called } // ✅ Correct: wrap functions to store them const [fn, setFn] = useState(() => someFunction); function handleClick() { setFn(() => someOtherFunction); } ```

Multiple ongoing Transitions are batched together

If there are multiple ongoing Transitions, React currently batches them together. This is a limitation that may be removed in a future release.

startTransition function signature and parameters

startTransition is a function that takes one parameter: action, which is a function that updates state by calling one or more set functions. startTransition does not return anything. React calls the action function immediately with no parameters and marks all state updates scheduled synchronously during the action function call as Transitions.

State updates after await in startTransition must be wrapped in additional startTransition

When you use await inside a startTransition function, state updates that happen after the await are not marked as Transitions. You must wrap state updates after each await in another startTransition call to mark them as Transitions. This is a known limitation that will be fixed in the future.

startTransition is non-blocking and prevents unwanted loading indicators

Transitions marked with startTransition are non-blocking, meaning they do not display unwanted loading indicators. The isPending flag switches to true at the first call to startTransition and stays true until all actions complete and the final state is shown to the user.

Cannot use Transitions for controlled input state

You cannot use Transitions for a state variable that controls an input, because Transitions are non-blocking but updating an input in response to the change event must happen synchronously. To handle this, either declare two separate state variables (one for synchronous input state, one for Transition state), or use useDeferredValue which will lag behind the real value.

useTransition can only be called inside components or custom Hooks

useTransition is a Hook and can only be called inside components or custom Hooks. If you need to start a Transition somewhere else, such as from a data library, use the standalone startTransition function instead.

State updates in Transitions only if scheduled during startTransition call

State updates are marked as Transitions only if they happen during the startTransition call itself. If you try to perform state updates in setTimeout or after the startTransition function has completed, they will not be marked as Transitions.

startTransition function has stable identity and can be omitted from Effect dependencies

The startTransition function has a stable identity, so it will often be seen omitted from Effect dependencies. Including it will not cause the Effect to fire. If the linter allows you to omit a dependency without errors, it is safe to do so.

State updates in Transitions are interrupted by other state updates

A state update marked as a Transition will be interrupted by other state updates. For example, if you update a chart component inside a Transition and then start typing into an input while the chart is re-rendering, React will restart the rendering work on the chart component after handling the input update.

useTransition example with startTransition

import { useState, useTransition } from 'react'; function CheckoutForm() { const [isPending, startTransition] = useTransition(); const [quantity, setQuantity] = useState(1); function onSubmit(newQuantity) { startTransition(async function () { const savedQuantity = await updateQuantity(newQuantity); startTransition(() => { setQuantity(savedQuantity); }); }); } // ... }

Functions passed to startTransition are called Actions

The function passed to startTransition is called an 'Action'. By convention, any callback called inside startTransition should be named 'action' or include the 'Action' suffix.

You cannot wrap Transitions only if you have access to the set function

You can wrap an update into a Transition only if you have access to the set function of that state. If you want to start a Transition in response to some prop or a custom Hook value, try useDeferredValue instead.

useTransition usage for non-blocking updates with Actions

Call useTransition at the top of your component to create Actions and access the pending state. Pass a function to startTransition to mark a state update as a Transition. The function passed to startTransition is called the 'Action'. You can update state and optionally perform side effects within an Action, and the work will be done in the background without blocking user interactions.

Transitions prevent interrupting user interactions

While a Transition is in progress, your UI stays responsive. For example, if the user clicks a tab but then changes their mind and clicks another tab, the second click will be immediately handled without waiting for the first update to finish.

Exposing action prop from components

You can expose an 'action' prop from a component to allow a parent to call an Action. When exposing an action prop, you should await it inside the transition. This allows the action callback to be either synchronous or asynchronous without requiring an additional startTransition to wrap the await.

Building a Suspense-enabled router with useTransition

When building a React framework or router, mark page navigations as Transitions. This makes Transitions interruptible (letting users click away without waiting), prevents unwanted loading indicators (avoiding jarring jumps), and waits for all pending actions before showing the new page. Example: function Router() { const [page, setPage] = useState('/'); const [isPending, startTransition] = useTransition(); function navigate(url) { startTransition(() => { setPage(url); }); } }

Displaying error to users with error boundary in Transitions

If a function passed to startTransition throws an error, you can display an error to your user with an error boundary. Wrap the component where you are calling useTransition in an error boundary. Once the function passed to startTransition errors, the fallback for the error boundary will be displayed.

Transitions do not delay function execution

The function you pass to startTransition is executed immediately, not delayed like setTimeout. React executes your function immediately, but any state updates scheduled while it is running are marked as Transitions.

Out of order state updates in Transitions with async requests

When using await inside startTransition, it is possible for previous requests to finish after later requests, causing state updates to happen out of order. This is expected behavior because Actions within a Transition do not guarantee execution order. For common use cases, use higher-level abstractions like useActionState and form actions which handle ordering for you. For advanced cases, implement your own queuing and abort logic.

useTransition hook signature and return value

useTransition is a React Hook called with no parameters that returns an array with exactly two items: (1) isPending, a boolean flag indicating whether there is a pending Transition, and (2) startTransition, a function that lets you mark updates as a Transition. The call signature is: const [isPending, startTransition] = useTransition()

Never pass around hooks as regular values

Hooks should only be called inside of components. Never pass hooks around as regular values. React is responsible for calling hooks when necessary.

Return values and arguments to Hooks are immutable

Once values are passed to a Hook, you should not modify them. Like props in JSX, values become immutable when passed to a Hook.

Hook return values are immutable

Values returned from hooks should not be mutated after receipt, as they may have been memoized. Mutating return values can cause the memoization logic to become incorrect and lead to stale data being used.

Example: Don't mutate hook arguments, make a copy instead

// Bad: function useIconStyle(icon) { const theme = useContext(ThemeContext); if (icon.enabled) { icon.className = computeStyle(icon, theme); } return icon; } // Good: function useIconStyle(icon) { const theme = useContext(ThemeContext); const newIcon = { ...icon }; if (icon.enabled) { newIcon.className = computeStyle(icon, theme); } return newIcon; } Create a copy of the argument using spread syntax instead of mutating the original.

Mutating hook arguments breaks memoization

If a custom hook uses its arguments as dependencies for memoization (like in useMemo), mutating those arguments after the hook call will cause the memoization to become incorrect. The hook won't detect the change because it only tracks the reference identity of the argument, not its contents. Always create a copy of hook arguments if modification is needed.

State values from useState are immutable - use setter function

useState returns a state variable and a setter function. The state variable should never be mutated directly. Directly modifying a state variable does not cause the component to update and leaves the UI outdated. Always use the setter function returned by useState to update state, which tells React to queue a re-render.

Example: Don't mutate state, use setState instead

// Bad: function Counter() { const [count, setCount] = useState(0); function handleClick() { count = count + 1; } return <button onClick={handleClick}>You pressed me {count} times</button>; } // Good: function Counter() { const [count, setCount] = useState(0); function handleClick() { setCount(count + 1); } return <button onClick={handleClick}>You pressed me {count} times</button>; } Use the setter function setCount to update state instead of mutating the count variable directly.

Hook arguments are immutable

Values passed as arguments to hooks should not be mutated. Like props in JSX, values become immutable when passed to a hook. Mutating hook arguments can break memoization and local reasoning about hook behavior, causing memoized values to become incorrect.

Example of correct Hook usage in components

function ChatInput() { return <Button /> } function Button() { const data = useDataWithLogging(); // ✅ Good: Use the Hook directly } function useDataWithLogging() { // If there's any conditional logic to change the Hook's behavior, it should be inlined into the Hook } This example shows the correct way to use Hooks by calling them directly within the component that needs them.

Example of incorrect Hook passing as prop

function ChatInput() { return <Button useData={useDataWithLogging} /> // 🔴 Bad: don't pass Hooks as props } This example shows the incorrect pattern of passing Hooks as props to other components.

Don't pass Hooks as props to other components

Never use dependency injection to pass a Hook as a prop to another component. Instead, inline the Hook call directly into the component that needs it and handle any conditional logic within that Hook.

Dynamic Hook usage increases complexity and breaks local reasoning

When Hooks are used dynamically or passed around, it increases the complexity of the app and inhibits local reasoning. This makes teams less productive long-term and makes it easier to accidentally break the Rules of Hooks. If Hooks need conditional behavior, the logic should be inlined into the Hook itself.

Example of incorrect higher order Hook pattern

function ChatInput() { const useDataWithLogging = withLogging(useData); // 🔴 Bad: don't write higher order Hooks const data = useDataWithLogging(); } This example shows the incorrect pattern of dynamically creating and mutating Hooks.

Violating direct component calls breaks Rules of Hooks

If a component contains Hooks, calling the component directly as a function in a loop or conditionally will violate the Rules of Hooks.

Never pass Hooks as regular values

Hooks should only be called inside of components or other Hooks. Never pass Hooks around as regular values or props. This enables local reasoning and allows developers to understand everything a component can do by looking at that component in isolation. Breaking this rule prevents React from automatically optimizing your component.

Don't dynamically mutate Hooks with higher order functions

Hooks should be static and immutable. Do not write higher order Hooks that mutate or wrap other Hooks dynamically. Instead, create a static version of the Hook with the desired functionality by inlining the logic into a new Hook function.

Example of correct Hook composition

function ChatInput() { const data = useDataWithLogging(); // ✅ Good: Create a new version of the Hook } function useDataWithLogging() { // ... Create a new version of the Hook and inline the logic here } This example shows the correct way to compose Hook functionality by creating a dedicated Hook function instead of dynamically mutating Hooks.

Prohibited locations for calling Hooks

Do not call Hooks inside conditions or loops. Do not call Hooks after a conditional return statement. Do not call Hooks in event handlers. Do not call Hooks in class components. Do not call Hooks inside functions passed to useMemo, useReducer, or useEffect. Do not call Hooks inside try/catch/finally blocks.

Custom Hooks can call other Hooks

Custom Hooks may call other Hooks because custom Hooks are also only supposed to be called while a function component is rendering.

Only call Hooks from React functions

Hooks must only be called from React function components or custom Hooks. Do not call Hooks from regular JavaScript functions. This ensures that all stateful logic in a component is clearly visible from its source code.

Only call Hooks at the top level

Hooks must be called at the top level of a function component or custom Hook, before any early returns. Do not call Hooks inside loops, conditions, nested functions, or try/catch/finally blocks. Hooks can only be called while React is rendering a function component.

Valid locations to call Hooks

Hooks may be called at the top level in the body of a function component, or at the top level in the body of a custom Hook.

Give your agent this brain