skip to content

Live Updates & Streams

Keeping the tree current when data arrives unasked: push events landing in the cache components already read, the merge choice per event, a catch-up read after a gap. Asked because pushes race reads.

on this pageshow

questions

6

When a push channel delivers data no component requested, where must those events land for the tree to update?

level: middleimportance: must knowfreq 56%

answer

  1. one place data lands
  2. a push is just another writer
  3. the bridge maps events to keys
  4. components read entries, not channels
  5. delete the channel; screen still works

basics

~20 s

In the same cache entries components already read. A push channel is just another writer of existing keys, so views re-render through their normal subscription and no component cares whether a value came from a request or an event.

solid answer

~40 s

They land in the shared cache or store entry that components already read — the same entry a response would have filled. A bridge layer sits between the channel and the cache: it takes an event, maps it to the key or keys it affects, merges it in, and lets the store's normal notification re-render whatever is subscribed. No component branches on the origin of a value, so a screen built on requests keeps working unchanged when a channel is added later. Two consequences follow. You still need an initial read, because a channel usually carries subsequent changes rather than current state. And the channel is owned by the cache layer, not by the component that happens to be on screen, because it has to outlive any one instance.

go deeper

for a junior

Remember the shape: events from the server are written into the same place your fetched data lives, and the screen updates because it was already reading that place.

for a middle

Explain the bridge: an event is mapped to the affected keys, merged into those entries, and the store's normal notification re-renders subscribers. Be able to say why component-local state breaks a second reader.

for a senior

Show that you scope the channel to the cache rather than to a view, that you still issue an initial read, and that you have a deliberate answer for events arriving on keys nobody is reading.

for a principal

Frame it as a contract: components read keys, and whether a value arrived by request or by push is below their line. That contract is what lets a team add live behaviour to existing screens without touching them.

## Pull and push in the same tree Almost all remote data in a component tree is **pulled**: something declares a request — a component on mount, a route boundary above it, a loader that runs before the view — the response fills a **cache entry** under some key, and components read that entry. Pull is comfortable because the tree decides when data moves, and every mechanism the framework gives you (loading state, deduplication, invalidation, refetch) is keyed to a request. A push channel inverts the trigger. The server emits a change and the client receives it with **no component having asked**. The interesting design question is therefore not how to open the channel — that is transport work, and the platform hands you the client — but **where the event lands**. ## The rule that makes live data usable **A pushed event is merged into the same entry a read fills.** That single rule is what keeps a live screen simple: - the bridge receives an event and maps it to the **key or keys** it affects; - it merges the event into those entries through the store's ordinary write path; - the store notifies subscribers exactly as it would after a response; - each component reads **one** value and re-renders. The test of whether you got it right: **delete the channel and the screen still works**; add it back and no component file changes. If a component has an `if` that asks where a value came from, the bridge is in the wrong place. ## Landing places compared | Where the event lands | What components see | What goes wrong | |---|---|---| | The receiving component's own state | live, but only inside that one component | other readers of the record stay stale; the value dies on unmount; the channel's life is tied to a view | | A second store read alongside the cache | two values per record | every consumer merges by hand, and each does it slightly differently | | A direct mutation of the rendered node | text changes on screen, state does not | the next render overwrites it, and nothing can read the value back | | The cache entry the reads already use | one value, one code path | you must choose a key mapping and a merge rule per event — the real work, made explicit | ## What the bridge layer actually does 1. **Resolve keys.** One event usually touches more than one entry: the record's own entry and the list entry that contains it, sometimes an aggregate. An event model that carries a stable resource identity makes this mechanical; one that carries only a display string does not. 2. **Choose a merge.** Replace the entry, patch a field, or invalidate it and let the next read refetch — decided per event shape. 3. **Write through the store**, not around it, so subscription notification, derived values and tooling all behave as they do for a response. 4. **Decide about unread keys.** An event may arrive for a key nothing is currently reading. Dropping it is legitimate; caching it is legitimate; doing so by accident is not, because a cache that accumulates every pushed key grows without bound. 5. **Expose its own connection state as ordinary state**, so a degraded channel is visible in the tree rather than silently stale. ## How the re-render is triggered differs by reactivity model Frameworks differ here, and the difference is invisible to the bridge. In a runtime that re-runs a component function on every state write, the store's notification schedules a re-run and the value is read again during it. In a runtime with fine-grained dependency tracking, the write marks exactly the derived values that read that entry and only those parts of the view update. In a compile-time runtime, the subscription was wired at build time and the write triggers the generated update path. In a dirty-checking runtime, the write has to reach the runtime's check cycle at all — the bridge must deliver the event inside the runtime's own mechanism rather than from a raw callback. All four converge on the same rule: write the entry, let the framework do the propagation. ## Consequences to plan for - **An initial read is still required.** Channels usually deliver changes, not current state; without a read, the screen renders empty until something happens to change. - **The channel outlives components.** It belongs to the cache layer, which survives navigation, not to the instance that first needed it. - **Reads stay the only interface.** Components subscribe to keys; the existence of a channel is an implementation detail below them. - **Ephemeral signals are a separate decision.** Something nothing will read back later does not belong in the cache at all.

  • Why should the channel be owned by the cache layer rather than by the component that first needs the data?
    Because its useful lifetime is longer than any one instance. Components mount and unmount on every navigation, so an instance-owned channel is torn down and reopened constantly, and events that arrive between the two are lost. A cache-owned channel is opened once for a key, shared by every reader, and closed when interest actually ends.
  • What happens if an event arrives for a key no component is currently reading?
    Nothing re-renders, and you must decide deliberately what the cache does. Merging it keeps a later reader correct but grows the store with data nobody asked for; dropping it keeps the store bounded but means the next read must fetch. A common middle ground is to merge only into entries that already exist and drop the rest.
  • How do you keep a component testable once its data can also arrive from a channel?
    By keeping the component a pure reader of a cache entry. Tests set the entry and assert the render; the bridge is tested separately by feeding it events and asserting the entries it produced. Nothing in the component needs a fake channel, because it never sees one.

A stadium scoreboard: the crowd reads the board, never the phone line into the press box. Both the score you asked for and the one that just came in are written to the same board.

saying these in an interview costs you the question

  • Storing pushed records in the receiving component's own local state
  • Opening a separate channel inside every component that shows live data
  • Assuming a channel removes the need for an initial read
  • Rendering pushed values from a list kept beside the fetched one
  • Treating an event as a command to re-render rather than a write to a key
  • Mutating the rendered node directly so the next render erases it
open as a page

A pushed update for a key arrives while a request for that same key is still in flight — which write wins?

level: seniorimportance: must knowfreq 52%

basics

~20 s

Not simply the last to arrive: a response reflects server state from when the request started, so it can be older than an event landing after it. Order writes by a server-owned revision, or refetch when none exists.

open as a page

How do you decide whether a pushed event should replace a cache entry, patch one field, or invalidate it?

level: middleimportance: should knowfreq 47%

basics

~20 s

By what the payload can support: a full resource replaces the entry, a partial change with a stable identity patches it, a bare notification only invalidates the affected keys. Never patch from a payload that leaves the record inconsistent.

open as a page

After a push channel drops and reconnects, why are the cache entries it fed wrong, and what restores them?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Because the changes made while disconnected were never delivered, so entries sit at pre-gap values with nothing marking them stale. Reconnect must resubscribe and trigger a catch-up read of the keys that still have readers.

open as a page

Several components read the same live key across navigations — how should the push subscription's lifetime be scoped?

level: seniorimportance: should knowfreq 44%

basics

~20 s

To the set of interested readers, not to one component. Reference-count per key in the cache layer: the first reader opens the subscription, later readers join it, and the last teardown closes it after a short grace window.

open as a page

How do you decide which pushed signals belong in the shared data cache and which stay ephemeral?

level: principalimportance: nice to knowfreq 31%

basics

~20 s

Ask whether anything reads the value back later. Server-owned records a later-mounting component must see belong in the cache. Presence, typing flags and live cursors are true only now, churn constantly, and belong in a short-lived ephemeral store.

open as a page