- Home
- /
- Tutorials
- /
- React Tutorial
- /
- Showing, Hiding and Losing State
Conditional Rendering
Showing, Hiding and Losing State
A component that stops being rendered is unmounted, and its state is thrown away. Hiding it with CSS keeps it alive. That difference is invisible until it bites.
Not rendered means gone
When a condition turns false, React removes the component from the tree. Its state, and anything it was holding, is discarded - bringing it back gives you a fresh component with its initial value.
State does not survive unmounting
jsx
function Counter({ label }) {
const [count, setCount] = React.useState(0)
return (
<button onClick={() => setCount(count + 1)}>
{label}: {count}
</button>
)
}
function Demo() {
const [show, setShow] = React.useState(true)
return (
<div>
<button onClick={() => setShow((s) => !s)}>
{show ? "Hide" : "Show"} the counters
</button>
<div style={{ marginTop: 10, display: "flex", gap: 8 }}>
{/* Unmounted when hidden — the count resets. */}
{show && <Counter label="Removed" />}
{/* Always mounted, just invisible — the count is kept. */}
<div style={{ display: show ? "block" : "none" }}>
<Counter label="Hidden with CSS" />
</div>
</div>
<p style={{ color: "#64748b" }}>
Click each counter up, hide, then show. Only the second remembers.
</p>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(<Demo />)Try it exactly as described. The first counter is back to zero; the second kept its value, because it never left the tree.
Which one you want
- Remove it when the state should reset - a closed dialog, a cancelled form, a different record.
- Hide it when the state must survive - a collapsed panel the user will reopen, a tab whose scroll position matters.
- Removing is the better default. Keeping invisible components alive costs memory and they keep running effects.
Resetting on purpose with key
Sometimes you want a component to reset even though it stays rendered - switching to a different user should clear the form, not carry the old text over. Changing the key tells React this is a different component instance.
key forces a fresh instance
jsx
function Editor({ userId }) {
const [text, setText] = React.useState("")
return (
<div>
<p>Editing user {userId}</p>
<input
value={text}
placeholder="type something"
onChange={(e) => setText(e.target.value)}
/>
</div>
)
}
function Demo() {
const [userId, setUserId] = React.useState(1)
return (
<div>
<button onClick={() => setUserId((id) => (id === 1 ? 2 : 1))}>
Switch user (currently {userId})
</button>
<div style={{ display: "flex", gap: 20, marginTop: 10 }}>
<div>
<b>No key</b>
<Editor userId={userId} />
</div>
<div>
<b>key={"{userId}"}</b>
<Editor key={userId} userId={userId} />
</div>
</div>
<p style={{ color: "#64748b" }}>Type in both, then switch. Only the keyed one clears.</p>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(<Demo />)This is the cleanest way to reset a subtree. It is also why key matters far beyond lists - it is React's notion of identity, and lists are only its most common use.
Hiding still costs something
A component hidden with CSS is fully alive. It renders on every parent update, its effects keep running, its timers keep firing and its subscriptions stay open. For a small panel that is nothing; for a heavy tab that polls a server it is real work happening off-screen.
It also stays in the accessibility tree unless you hide it properly. display: none removes it from screen readers; opacity or moving it off-screen does not, so a keyboard user can tab into content nobody can see.
- Removing unmounts: state is discarded and effects clean up.
- Hiding keeps everything running and only changes what is painted.
- Prefer removing unless the state genuinely has to survive.
- Change
keyto reset a component that stays rendered.
