Preserved
- What React sees
<Counter />in the same position before and after.- Result
- The existing state remains.
React attaches state to a component's place in the UI tree. Keep the same component identity in that place to preserve state, or change its identity when a fresh start is intentional.
A state variable is not stored inside a JSX tag or component function. React stores it and associates it with an identity under a particular parent. By default, that identity comes from the component's type and position among its siblings. A key can become part of the identity.
Counter and Profile are different component types.Useful shortcut: same type + same position + same key means preserve state. A different type, position, or key can create a new identity and reset state.
When React renders the same component type in the same position, it reuses that component instance and its state. Changing props or changing how the JSX was calculated does not reset the state by itself.
Counter stays the first child of the same main, so its score survives.import { useState } from "react";
function Counter({ isFancy }) {
const [score, setScore] = useState(0);
return (
<button
className={isFancy ? "fancy" : "plain"}
onClick={() => setScore(score + 1)}
>
Score: {score}
</button>
);
}
export default function Scoreboard() {
const [isFancy, setIsFancy] = useState(false);
return (
<main>
<Counter isFancy={isFancy} />
<label>
<input
type="checkbox"
checked={isFancy}
onChange={event => setIsFancy(event.target.checked)}
/>
Fancy style
</label>
</main>
);
}
React cares about the resulting UI tree, not whether the JSX appears in one branch, a variable, or a helper expression. If the resulting component matches the previous identity, its state is preserved.
When a component at a position is removed or replaced by a different type, React destroys its state. If the original component returns later, it is a new instance initialized from scratch. State in its descendant components is removed too.
<Counter /> in the same position before and after.<Counter /> replaced by <p>, or by a new key.0.function Scoreboard() {
const [showCounter, setShowCounter] = useState(true);
return (
<main>
{showCounter ? <Counter /> : <p>Counter is hidden.</p>}
<button onClick={() => setShowCounter(!showCounter)}>
Toggle counter
</button>
</main>
);
}
Avoid defining components inside other components. A nested component function is created again on every parent render. React sees a different component type and resets that subtree's state. Define component functions at the top level instead.
Keys are best known for identifying list items, but they can identify any sibling component. A key tells React to use the key instead of sibling order when matching an element with its previous identity.
function Game() {
const [player, setPlayer] = useState("Mira");
return (
<main>
<Counter key={player} player={player} />
<button
onClick={() => setPlayer(player === "Mira" ? "Noah" : "Mira")}
>
Next player
</button>
</main>
);
}
The transition from key="Mira" to key="Noah" tells React that the old counter and new counter are different identities. React unmounts the old subtree, creates a new one, and runs its state initializers.
function Messenger({ selectedContact }) {
return (
<Chat
key={selectedContact.id}
contact={selectedContact}
/>
);
}
function Chat({ contact }) {
const [draft, setDraft] = useState("");
return (
<textarea
aria-label={`Message ${contact.name}`}
value={draft}
onChange={event => setDraft(event.target.value)}
/>
);
}
A key only needs to distinguish siblings under that parent. It is not a global ID.
React uses it internally. Pass a separate prop if the child needs the same value.
Use an entity ID when identity follows data. Do not use a random value on every render because that forces repeated resets.
Keys are useful for forms, games, editors, and other stateful subtrees that should restart when their subject changes.
Keep this model: React preserves state for the same component identity under the same parent. Removing it, changing its type, or changing its key gives React a new identity and resets that subtree.
No. If the component keeps the same type, position, and key under its parent, React preserves its state and renders it with the new props.
<Chat key={contact.id} /> clear the draft?
The new key creates a different component identity. React removes the previous Chat subtree and mounts a new one, so useState("") supplies a fresh draft.
The parent creates a new component function on each render. React sees that function as a different component type, replaces the old subtree, and resets its state.