React Forum
Blog & Guide Responses
Feb 04, 2023 · 04:00 AM

Sharing state between components - discussion (2023-02-04)

Started by Mina Calder
externalstatelifting statecompositionroute docscontractsderived stateevidence

The "Sharing state between components" page is the canonical "lift it up" story, but real apps end up with more nuance: routes, panels, drafts, and derived outputs that multiple components must agree on. In the React Tips vocabulary, I like thinking in terms of explicit contracts: where does truth live, and what evidence do we render when components disagree?

Read the page

When you share state across panels/routes, do you still lift to a parent component or do you use a route document/vault? How do you keep shared state from becoming a global junk drawer? Do you store derived "shared outputs" (counts, selections) so UIs stay consistent, or compute them live?

Comments (18)

Back to latest
Marisol Vega
Feb 07, 2023 · 07:07 AM

We still lift to a parent when the relationship is local and the parent is stable.

But for route-level sharing, we use a route doc so back/forward replays it and the contract is explicit.

Dmitri Kline
Feb 10, 2023 · 10:10 AM

Long-form take: the "lift up" story breaks down when there isn't a single natural parent (e.g. list + detail + overlay). In those cases, a route doc is basically the "parent"—a stable place where state lives and can be observed. We log shared-state transitions so disagreements are visible:

txt
[tips] sharedState=selection set=doc_19 reason=list:click
[tips] sharedState=selection set=null reason=overlay:close
Keira Santos
Feb 16, 2023 · 04:16 PM

Counterpoint: route docs/vaults are easy to abuse. Everything becomes shared state because it's convenient.

We require a clear reason for shared state: either navigation, cross-panel coherence, or persistence across remount.

Mina Calder
Feb 19, 2023 · 04:04 AM

Yes. Shared state should be a contract, not a convenience.

If it isn't part of the user's experience, it probably shouldn't be shared at all.

Grace McCoy
Feb 28, 2023 · 10:22 PM

We store derived shared outputs when they are used in multiple places (badge counts, selection summaries).

Otherwise components compute them slightly differently and the UI feels inconsistent.

Jasper Nguyen
Mar 14, 2023 · 02:14 PM

For "global junk drawer", we split state by domain documents rather than one mega store.

Selection doc, filter doc, composer doc. Each has a small vocabulary and evidence keys.

Nina Kapoor
Mar 29, 2023 · 03:03 AM

One trick: give shared state an identity boundary and log it when it changes. It prevents mysterious cross-panel resets:

txt
[tips] identity=project:42 selection cleared reason=identity:change
Camille Duarte
Apr 18, 2023 · 06:18 PM

Long-form argument: shared state should be "replayable".

If back/forward can't replay it, it isn't really part of the route experience. It's hidden state.

Route docs solve that by being explicit about what gets replayed.

Owen Price
May 10, 2023 · 10:10 AM

Counterpoint: lifting to a parent is still the best when it's possible. It's simple and it stays local.

I only reach for docs when multiple routes/panels truly need to agree.

Benji Rios
Jun 01, 2023 · 06:06 AM

We rendered a "shared contract" evidence line in debug mode so disagreements were obvious:

txt
shared: selection=doc_19 filter=status:open panel=detail
Lina Ortega
Jun 25, 2023 · 10:22 PM

Docs tie-in: the state management and patterns pages are the best references for contract-driven shared state.

Related: State Management (Best Practices) and Patterns Library.

Amina Farouk
Jul 21, 2023 · 07:07 AM

We treated drafts as multi-truth: local draft in component, committed truth in route doc on blur/submit.

Sharing drafts globally made typing laggy and made undo/redo weird.

Evan Brooks
Aug 19, 2023 · 07:19 PM

Counterpoint: docs/vaults can make testing harder if you don't expose evidence in the UI.

If the UI renders lane and selection, tests stay simple. If not, tests reach into internals and become brittle.

Harper Iqbal
Sep 18, 2023 · 12:12 PM

We also used a merge rule when multiple panels write selection at once (newer wins, but log conflicts). Conflicts shouldn't be silent:

txt
[tips] sharedState conflict key=selection winner=panel:list loser=panel:overlay
Priya Liao
Oct 15, 2023 · 03:15 PM

The page's mental model is still correct: pick one place for truth.

In real apps, the trick is choosing a place that matches navigation and user intent boundaries.

Theo Bennett
Nov 12, 2023 · 04:04 AM

Long-form counterpoint: some shared state should not be shared at all; it should be derived independently from a stable input.

If you share a derived count and it becomes stale, you get trust issues. Sometimes recomputing is safer than caching.

Salma Qureshi
Dec 20, 2023 · 06:18 PM

The repo's emphasis on evidence is what makes shared state survivable.

If you share state and you can't see it, debugging becomes folklore.

Rowan Pierce
Dec 29, 2023 · 09:09 AM

We started small: share selection only. Everything else stayed local until we had a clear need.

That prevented the store from turning into a dumping ground.

Tomas Rivera
Dec 31, 2023 · 09:09 AM

If you're implementing shared state, write down the contract first: keys, identity boundaries, and evidence lines.

Then code it. Otherwise shared state grows by accident.