A collapsible panel's form loses everything typed into it whenever the panel is collapsed and reopened. Why, and what are the options?
answer
- existence, not visibility
- absent from the description means unmounted
- cleanup runs, instances are discarded
- lift the data above the conditional
- hiding keeps effects alive too
basics
~20 sA conditional that leaves the subtree out of the described output unmounts it: instance state, effects and host nodes are destroyed, so reopening builds a fresh form. Either hide it instead, or hold the values above the conditional.
solid answer
~50 sCollapsing the panel almost certainly removes its subtree from the described output. A conditional in a template or a branch in a render function does not hide anything — when the subtree is absent, the runtime **unmounts** it: component instances are destroyed with their state, cleanup for their effects runs, and the host nodes are removed along with whatever the host itself was holding, such as typed text in an unmanaged field, focus, scroll offset and in-flight transitions. Reopening runs a first render, so the form is new and empty. Three fixes, best first: lift the values into state living **above** the conditional, so the subtree becomes a view of data that outlives it; keep the subtree described and hide it instead, accepting that its effects keep running; or save the values during cleanup and restore them on the next mount, which is the fiddliest.
go deeper
Learn the rule behind the symptom: if the output no longer describes a subtree, that subtree is destroyed along with its state, and reopening creates a fresh one.
Explain the unmount sequence — cleanup, instance discarded, host nodes removed — and why values held above the conditional survive while values held inside it cannot.
Choose between lifting and hiding from what must survive and what must stop, and name the cost you accept: running effects and an accessibility obligation for hidden content.
Push the question upstream. Which state is application data that should never live inside a collapsible view, and what convention keeps every panel in the product answering that the same way?
This is the most common consequence of the fact that a conditional in described output is not a visibility switch. It decides whether the subtree **exists**, and existence is what instance state is tied to. ## What actually happens on collapse When the condition turns false, the next render's description simply has no such subtree. The runtime compares that with what it had and concludes the whole branch is gone, so it: 1. runs cleanup for every effect, subscription, timer and listener registered by the components inside; 2. discards those component instances, and with them all state they held; 3. removes the host nodes. Anything the host itself was keeping on those nodes goes with them: - the current text of a field the framework was not controlling; - which element had focus, and any selection or caret position inside it; - the scroll offset of a scrollable region; - a transition or animation in progress; - an open native control, such as a date or file picker. On reopen the runtime performs a **first render** of a brand-new instance, so every value starts at its initial state. Nothing is broken; the code asked for exactly this. ## The three ways out | approach | what survives | what it costs | |---|---|---| | lift the values above the conditional | every value you lifted, by construction | the state must be modelled somewhere durable | | keep it described, hide it instead | state and nodes, plus scroll, focus and animations if the node stays rendered | effects and subscriptions keep running; nodes stay in the tree | | save on cleanup, restore on mount | whatever you remembered to save | most code, easiest to drift out of sync | **Lifting** is the fix that scales. Move the form's values to state owned by an ancestor that is not inside the conditional — or to a store, or to the URL when the value belongs in a shareable address — and the subtree becomes a view over data whose lifetime is independent of it. This is usually also the right model: draft form values that matter are application state, not a rendering detail. **Hiding** keeps the instance alive because the subtree is still described. It is the right answer when what must survive is not just values but host-held state — a half-scrolled list, an editor with an undo stack — which lifting cannot reproduce. One caveat: a hide that takes the element out of rendering altogether still drops focus, so preserving focus depends on which hiding mechanism you choose. Be deliberate about the costs: effects, polling and subscriptions inside keep firing, the nodes stay in the tree with their memory and layout cost, and you must ensure hidden content is genuinely removed from the accessibility tree and from keyboard focus order rather than merely made invisible, or keyboard and screen-reader users will land inside a collapsed panel. **Save and restore** is a last resort. You persist the values as part of cleanup and rehydrate them on the next mount. It works, but every new field is a new thing to remember, and there is no compiler to tell you that you forgot one. ## Diagnosing it in the wild The symptom is easy to confirm. Watch whether the subtree's setup work runs again on reopen — a mount-time fetch firing a second time, a log line from initialisation, cleanup running on collapse. If it does, you are unmounting. If the values come back but focus and scroll do not, you have lifted state but the host nodes are still being recreated. A related variant is worth recognising: state can also vanish without a conditional at all, when the runtime concludes the instance at a position is a different instance than before and therefore replaces it rather than updating it. ## The judgment being tested The weak answer is to reach for hiding immediately, because it makes the symptom disappear with one line. The strong answer separates the two questions. *What must survive?* If it is data, lift it, because that is where the data belonged anyway. If it is host-held interaction state you cannot reconstruct, hide it and pay the running cost knowingly. *What must stop when the panel closes?* If there is a subscription, a poll or a video inside that must not keep running while collapsed, hiding is actively wrong and you need the unmount plus lifted state. Stating both halves — and naming the accessibility obligation that comes with hiding — is what distinguishes an engineer who has shipped this from one who has read about it.
- If you hide the panel instead of removing it, what keeps running that a reviewer should question?Everything the subtree started: subscriptions, polling intervals, timers, observers, animation loops, and any media that was playing. A hidden panel still costs those. You also inherit the obligation to keep hidden content out of the accessibility tree and out of keyboard focus order.
- Why is lifting the values usually better than saving and restoring them?Because lifting makes correctness structural: the data lives outside the subtree, so it cannot be lost. Save-and-restore relies on a list of fields that must be kept in step with the form by hand, and the failure mode is silent — a newly added field is simply forgotten.
- Which kinds of state can hiding preserve that lifting cannot?State the host owns rather than your component: text selection and caret position, scroll offsets, in-progress transitions, a native control's open state, an embedded editor's history, and focus provided the hiding mechanism leaves the node rendered. Lifting reproduces values, not the live node those things hang off.
- How would you confirm the subtree is being unmounted rather than merely re-rendered?Look for setup work repeating: initialisation logging on reopen, a mount-time request issued again, cleanup firing on collapse. A re-render keeps the same instance and runs none of that. If mount and cleanup both appear, the branch is being removed and recreated.
saying these in an interview costs you the question
- Says a conditional hides the subtree rather than removing it
- Reaches for hiding without asking what must stop running
- Thinks re-rendering and remounting destroy state alike
- Expects focus and scroll to survive because the values did
- Hides content visually and leaves it focusable and announced