--- title: Preserving and Resetting State --- State is isolated between components. React keeps track of which state belongs to which component based on their place in the UI tree. You can control when to preserve state and when to reset it between re-renders. * When React chooses to preserve or reset the state * How to force React to reset component's state * How keys and types affect whether the state is preserved ## State is tied to a position in the render tree {/*state-is-tied-to-a-position-in-the-tree*/} React builds [render trees](learn/understanding-your-ui-as-a-tree#the-render-tree) for the component structure in your UI. When you give a component state, you might think the state "lives" inside the component. But the state is actually held inside React. React associates each piece of state it's holding with the correct component by where that component sits in the render tree. Here, there is only one `` JSX tag, but it's rendered at two different positions: ```js import { useState } from 'react'; export default function App() { const counter = ; return (
{counter} {counter}
); } function Counter() { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return (
setHover(true)} onPointerLeave={() => setHover(false)} >

{score}

); } ``` ```css label { display: block; clear: both; } .counter { width: 100px; text-align: center; border: 1px solid gray; border-radius: 4px; padding: 20px; margin: 0 20px 20px 0; float: left; } .hover { background: #ffffd8; } ```
Here's how these look as a tree: React tree **These are two separate counters because each is rendered at its own position in the tree.** You don't usually have to think about these positions to use React, but it can be useful to understand how it works. In React, each component on the screen has fully isolated state. For example, if you render two `Counter` components side by side, each of them will get its own, independent, `score` and `hover` states. Try clicking both counters and notice they don't affect each other: ```js import { useState } from 'react'; export default function App() { return (
); } function Counter() { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return (
setHover(true)} onPointerLeave={() => setHover(false)} >

{score}

); } ``` ```css .counter { width: 100px; text-align: center; border: 1px solid gray; border-radius: 4px; padding: 20px; margin: 0 20px 20px 0; float: left; } .hover { background: #ffffd8; } ```
As you can see, when one counter is updated, only the state for that component is updated: Updating state React will keep the state around for as long as you render the same component at the same position in the tree. To see this, increment both counters, then remove the second component by unchecking "Render the second counter" checkbox, and then add it back by ticking it again: ```js import { useState } from 'react'; export default function App() { const [showB, setShowB] = useState(true); return (
{showB && }
); } function Counter() { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return (
setHover(true)} onPointerLeave={() => setHover(false)} >

{score}

); } ``` ```css label { display: block; clear: both; } .counter { width: 100px; text-align: center; border: 1px solid gray; border-radius: 4px; padding: 20px; margin: 0 20px 20px 0; float: left; } .hover { background: #ffffd8; } ```
Notice how the moment you stop rendering the second counter, its state disappears completely. That's because when React removes a component, it destroys its state. Deleting a component When you tick "Render the second counter", a second `Counter` and its state are initialized from scratch (`score = 0`) and added to the DOM. Adding a component **React preserves a component's state for as long as it's being rendered at its position in the UI tree.** If it gets removed, or a different component gets rendered at the same position, React discards its state. ## Same component at the same position preserves state {/*same-component-at-the-same-position-preserves-state*/} In this example, there are two different `` tags: ```js import { useState } from 'react'; export default function App() { const [isFancy, setIsFancy] = useState(false); return (
{isFancy ? ( ) : ( )}
); } function Counter({ isFancy }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } if (isFancy) { className += ' fancy'; } return (
setHover(true)} onPointerLeave={() => setHover(false)} >

{score}

); } ``` ```css label { display: block; clear: both; } .counter { width: 100px; text-align: center; border: 1px solid gray; border-radius: 4px; padding: 20px; margin: 0 20px 20px 0; float: left; } .fancy { border: 5px solid gold; color: #ff6767; } .hover { background: #ffffd8; } ```
When you tick or clear the checkbox, the counter state does not get reset. Whether `isFancy` is `true` or `false`, you always have a `` as the first child of the `div` returned from the root `App` component: Updating the `App` state does not reset the `Counter` because `Counter` stays in the same position It's the same component at the same position, so from React's perspective, it's the same counter. Remember that **it's the position in the UI tree--not in the JSX markup--that matters to React!** This component has two `return` clauses with different `` JSX tags inside and outside the `if`: ```js import { useState } from 'react'; export default function App() { const [isFancy, setIsFancy] = useState(false); if (isFancy) { return (
); } return (
); } function Counter({ isFancy }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } if (isFancy) { className += ' fancy'; } return (
setHover(true)} onPointerLeave={() => setHover(false)} >

{score}

); } ``` ```css label { display: block; clear: both; } .counter { width: 100px; text-align: center; border: 1px solid gray; border-radius: 4px; padding: 20px; margin: 0 20px 20px 0; float: left; } .fancy { border: 5px solid gold; color: #ff6767; } .hover { background: #ffffd8; } ```
You might expect the state to reset when you tick checkbox, but it doesn't! This is because **both of these `` tags are rendered at the same position.** React doesn't know where you place the conditions in your function. All it "sees" is the tree you return. In both cases, the `App` component returns a `
` with `` as a first child. To React, these two counters have the same "address": the first child of the first child of the root. This is how React matches them up between the previous and next renders, regardless of how you structure your logic. ## Different components at the same position reset state {/*different-components-at-the-same-position-reset-state*/} In this example, ticking the checkbox will replace `` with a `

`: ```js import { useState } from 'react'; export default function App() { const [isPaused, setIsPaused] = useState(false); return (

{isPaused ? (

See you later!

) : ( )}
); } function Counter() { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return (
setHover(true)} onPointerLeave={() => setHover(false)} >

{score}

); } ``` ```css label { display: block; clear: both; } .counter { width: 100px; text-align: center; border: 1px solid gray; border-radius: 4px; padding: 20px; margin: 0 20px 20px 0; float: left; } .hover { background: #ffffd8; } ``` Here, you switch between _different_ component types at the same position. Initially, the first child of the `
` contained a `Counter`. But when you swapped in a `p`, React removed the `Counter` from the UI tree and destroyed its state. When `Counter` changes to `p`, the `Counter` is deleted and the `p` is added When switching back, the `p` is deleted and the `Counter` is added Also, **when you render a different component in the same position, it resets the state of its entire subtree.** To see how this works, increment the counter and then tick the checkbox: ```js import { useState } from 'react'; export default function App() { const [isFancy, setIsFancy] = useState(false); return (
{isFancy ? (
) : (
)}
); } function Counter({ isFancy }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } if (isFancy) { className += ' fancy'; } return (
setHover(true)} onPointerLeave={() => setHover(false)} >

{score}

); } ``` ```css label { display: block; clear: both; } .counter { width: 100px; text-align: center; border: 1px solid gray; border-radius: 4px; padding: 20px; margin: 0 20px 20px 0; float: left; } .fancy { border: 5px solid gold; color: #ff6767; } .hover { background: #ffffd8; } ```
The counter state gets reset when you click the checkbox. Although you render a `Counter`, the first child of the `div` changes from a `section` to a `div`. When the child `section` was removed from the DOM, the whole tree below it (including the `Counter` and its state) was destroyed as well. When `section` changes to `div`, the `section` is deleted and the new `div` is added When switching back, the `div` is deleted and the new `section` is added As a rule of thumb, **if you want to preserve the state between re-renders, the structure of your tree needs to "match up"** from one render to another. If the structure is different, the state gets destroyed because React destroys state when it removes a component from the tree. This is why you should not nest component function definitions. Here, the `MyTextField` component function is defined *inside* `MyComponent`: ```js {expectedErrors: {'react-compiler': [7]}} import { useState } from 'react'; export default function MyComponent() { const [counter, setCounter] = useState(0); function MyTextField() { const [text, setText] = useState(''); return ( setText(e.target.value)} /> ); } return ( <> ); } ``` Every time you click the button, the input state disappears! This is because a *different* `MyTextField` function is created for every render of `MyComponent`. You're rendering a *different* component in the same position, so React resets all state below. This leads to bugs and performance problems. To avoid this problem, **always declare component functions at the top level, and don't nest their definitions.** ## Resetting state at the same position {/*resetting-state-at-the-same-position*/} By default, React preserves state of a component while it stays at the same position. Usually, this is exactly what you want, so it makes sense as the default behavior. But sometimes, you may want to reset a component's state. Consider this app that lets two players keep track of their scores during each turn: ```js import { useState } from 'react'; export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return (
{isPlayerA ? ( ) : ( )}
); } function Counter({ person }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return (
setHover(true)} onPointerLeave={() => setHover(false)} >

{person}'s score: {score}

); } ``` ```css h1 { font-size: 18px; } .counter { width: 100px; text-align: center; border: 1px solid gray; border-radius: 4px; padding: 20px; margin: 0 20px 20px 0; } .hover { background: #ffffd8; } ```
Currently, when you change the player, the score is preserved. The two `Counter`s appear in the same position, so React sees them as *the same* `Counter` whose `person` prop has changed. But conceptually, in this app they should be two separate counters. They might appear in the same place in the UI, but one is a counter for Taylor, and another is a counter for Sarah. There are two ways to reset state when switching between them: 1. Render components in different positions 2. Give each component an explicit identity with `key` ### Option 1: Rendering a component in different positions {/*option-1-rendering-a-component-in-different-positions*/} If you want these two `Counter`s to be independent, you can render them in two different positions: ```js import { useState } from 'react'; export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return (
{isPlayerA && } {!isPlayerA && }
); } function Counter({ person }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return (
setHover(true)} onPointerLeave={() => setHover(false)} >

{person}'s score: {score}

); } ``` ```css h1 { font-size: 18px; } .counter { width: 100px; text-align: center; border: 1px solid gray; border-radius: 4px; padding: 20px; margin: 0 20px 20px 0; } .hover { background: #ffffd8; } ```
* Initially, `isPlayerA` is `true`. So the first position contains `Counter` state, and the second one is empty. * When you click the "Next player" button the first position clears but the second one now contains a `Counter`. Initial state Clicking "next" Clicking "next" again Each `Counter`'s state gets destroyed each time it's removed from the DOM. This is why they reset every time you click the button. This solution is convenient when you only have a few independent components rendered in the same place. In this example, you only have two, so it's not a hassle to render both separately in the JSX. ### Option 2: Resetting state with a key {/*option-2-resetting-state-with-a-key*/} There is also another, more generic, way to reset a component's state. You might have seen `key`s when [rendering lists.](/learn/rendering-lists#keeping-list-items-in-order-with-key) Keys aren't just for lists! You can use keys to make React distinguish between any components. By default, React uses order within the parent ("first counter", "second counter") to discern between components. But keys let you tell React that this is not just a *first* counter, or a *second* counter, but a specific counter--for example, *Taylor's* counter. This way, React will know *Taylor's* counter wherever it appears in the tree! In this example, the two ``s don't share state even though they appear in the same place in JSX: ```js import { useState } from 'react'; export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return (
{isPlayerA ? ( ) : ( )}
); } function Counter({ person }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return (
setHover(true)} onPointerLeave={() => setHover(false)} >

{person}'s score: {score}

); } ``` ```css h1 { font-size: 18px; } .counter { width: 100px; text-align: center; border: 1px solid gray; border-radius: 4px; padding: 20px; margin: 0 20px 20px 0; } .hover { background: #ffffd8; } ```
Switching between Taylor and Sarah does not preserve the state. This is because **you gave them different `key`s:** ```js {isPlayerA ? ( ) : ( )} ``` Specifying a `key` tells React to use the `key` itself as part of the position, instead of their order within the parent. This is why, even though you render them in the same place in JSX, React sees them as two different counters, and so they will never share state. Every time a counter appears on the screen, its state is created. Every time it is removed, its state is destroyed. Toggling between them resets their state over and over. Remember that keys are not globally unique. They only specify the position *within the parent*. ### Resetting a form with a key {/*resetting-a-form-with-a-key*/} Resetting state with a key is particularly useful when dealing with forms. In this chat app, the `` component contains the text input state: ```js src/App.js import { useState } from 'react'; import Chat from './Chat.js'; import ContactList from './ContactList.js'; export default function Messenger() { const [to, setTo] = useState(contacts[0]); return (
setTo(contact)} />
) } const contacts = [ { id: 0, name: 'Taylor', email: 'taylor@mail.com' }, { id: 1, name: 'Alice', email: 'alice@mail.com' }, { id: 2, name: 'Bob', email: 'bob@mail.com' } ]; ``` ```js src/ContactList.js export default function ContactList({ selectedContact, contacts, onSelect }) { return (
    {contacts.map(contact =>
  • )}
); } ``` ```js src/Chat.js import { useState } from 'react'; export default function Chat({ contact }) { const [text, setText] = useState(''); return (