A click here changes something there
This is the feature that turns four panels into a cockpit: click a row in the screener and the chart, the quote and the backtest all follow.
One shared concept, owned by the container
type Selection = { symbol: string | null; source: string };
One field for what, one for which panel said so. The container owns it; panels receive it and call onSelect.
The source field looks like decoration and is not. Without it a panel cannot tell whether the selection came from itself, so it cannot avoid reacting to its own click — and that is the loop this pattern is famous for.
The loop, concretely
Panel A calls onSelect("AAPL.US"). The container updates selection. Panel A re-renders, sees a new selection, and calls onSelect again. If anything in that path is asynchronous or debounced, you now have a cockpit that fetches forever and a meter that climbs while nobody touches the page.
Two rules stop it, and you want both:
Panels report events, they do not react to their own. Check source before responding.
onSelect with the current value is a no-op. Compare before setting state. One line in the container, and it makes the whole system tolerant of panels that get it wrong.
Not everything should follow
The tempting version is that every panel tracks the selection. Resist it, because a cockpit where everything moves at once is one you cannot read.
Let each panel declare its own behaviour: follows: true for a chart or a quote, false for a watchlist you deliberately keep still. Put that in the panel's config, so it is saved with the layout and the person decides — you will want the same panel following in one layout and pinned in another.
Selection is not persisted
It belongs in the shared tier from unit 1, not the persisted one. Reloading the page should not restore what you happened to be looking at yesterday; it should restore your layout.
The exception is the URL. Putting the selection in the query string makes a view shareable and survives a refresh without going near the database — and it is the same "URL is untrusted input" rule from course 2, so validate the symbol against your allowlist before anything touches it.
Try it now
Wire selection between two panels, then deliberately remove the no-op guard and watch your usage meter. Seeing the counter climb from a feedback loop you built is the most memorable version of this lesson.