Sharing state between components - discussion (2023-02-16)
The sharing-state page is the canonical "lift state up" story, but the real-world tension is: when does "lift to a parent" stop working and when do you need a route document or vault because there isn't a single natural parent (list + detail + overlay)? In the React Tips-ish posture, I like treating shared state as a contract: one place for truth, plus evidence so you can tell when panels disagree.
When you share state across panels, do you still lift to a component parent, or do you use a route doc/vault? What shared outputs do you store as derived (counts, selection summaries) to keep UI consistent? How do you prevent shared state from becoming a global junk drawer?
Comments (14)
Back to latestWe still lift to a parent when it’s truly local.
But the moment the state must survive route motion or be replayable with back/forward, it stops being "just a parent" and becomes a document.
We log shared-state transitions so "why did selection change" isn't a detective story:
txt
[tips] sharedState=selection set=doc_19 reason=list:click
[tips] sharedState=selection set=null reason=overlay:close
[tips] sharedState=filter set=status:open reason=tab:click
And we render a compact evidence summary: selection=doc_19 filter=status:open panel=detail.
Counterpoint: route docs and vaults are easy to abuse. Everything becomes shared state because it's convenient.
We require one of three reasons before sharing: navigation replay, cross-panel coherence, or persistence across remount boundaries.
That's the right gating question: what user behavior requires this to be shared?
If the answer is "it was easier", it probably shouldn't be shared at all.
Stored-derived shared outputs were the biggest coherence win: selection summary, counts, and current posture string.
Without those, each panel re-derives and you get subtle mismatches that feel like data corruption.
Long-form: shared state is less about sharing variables and more about sharing *meaning*. If two panels need to agree on what "selected" means, you need a single surface where selection identity lives and a single vocabulary for selection changes.
The fastest way to make shared state go wrong is to share raw inputs but not share derived meaning (summary, lane, source). That's when panel A thinks selection is a draft and panel B thinks it's committed, and nobody can explain the UI.
We prevented junk-drawer stores by splitting into small domain docs: selection doc, filter doc, composer doc.
Each doc has a minimal schema and evidence keys. If a value doesn't belong in one of them, it probably shouldn't be shared.
Counterpoint: lifting to a parent is still underrated. It's explicit and local and doesn't require governance.
I reach for docs/vaults only when there truly isn't a parent component that represents the shared surface.
Long-form counterpoint: shared state becomes dangerous when writes can come from multiple places without a merge rule. We introduced a merge policy (newer wins, but conflicts are logged) and it changed everything:
txt
[tips] sharedState conflict key=selection winner=panel:list loser=panel:overlay reason=newerWrite
Silent conflicts are where bugs go to live.
Docs tie-in: the state and patterns docs are a good companion for contract-driven shared state.
Related: State Management (Best Practices) and Patterns Library.
We treated drafts as multi-truth and committed on boundaries (blur/submit).
Sharing drafts globally made typing slower and made it impossible to reason about what the user actually committed.
We render evidence in a visually-hidden contract line so tests can query it by text.
That kept us from coupling tests to attribute selectors while still making shared state visible.
A small governance trick: every shared doc has an owner and an intended audience (UI vs routing vs support).
If a key doesn't have an audience, it doesn't get added.
We also treat shared outputs as part of the product contract (counts, selection summary).
If those are derived in multiple places, the product will look inconsistent even if raw data is correct.
If you want shared state to be healthy: define the contract, define the merge rule, render evidence.
The page's "one source of truth" idea is still right; the real work is choosing what the truth is and how it's observed.