skip to content

Outputs & Upward Events

How a child talks to its parent without owning its state: a callback invoked, a named event with a payload, two-way binding as sugar over both. Upward flow is where designs go wrong.

on this pageshow

questions

6

In a component framework, what are the two usual ways a child component signals something upward to its parent?

level: juniorimportance: must knowfreq 78%

answer

  1. data down, signals up
  2. a function is just another input
  3. a declared name plus a payload
  4. the child reports, the parent decides

basics

~20 s

Either the parent passes a function down as an input and the child calls it, or the child declares a named output event that the parent binds a handler to. Both leave the state itself owned by the parent.

solid answer

~40 s

Upward communication has two shapes. The parent can pass a function down as an ordinary input; the child invokes it with whatever the parent needs to know, so the call is direct, synchronous, and can even return a value the child reads. Or the child can declare a named output - `item-selected` with a payload, say - and the runtime routes each emission to the handler bound where the child was used. In that shape the child cannot see what the handler does. Either way the child reports a fact (`the user picked row 7`), and the parent decides what it means; the child never writes into the parent's state. Which shape a framework prefers is a style choice - the contract underneath is the same.

go deeper

for a junior

Recall both shapes: a function handed down that the child calls, or a named event the child emits and the parent handles. Data flows down as inputs, signals flow up.

for a middle

Explain the difference in coupling. A called function is direct, ordered, and can return a value; a declared event is a name the runtime routes, leaving the child blind to what the handler does.

for a senior

Show payload design judgment: report the fact rather than the intended action, keep the event list small enough to read as a contract, and make the component behave when nothing is bound.

for a principal

Weigh the two shapes as API surface across many teams. A declared event list is discoverable and reviewable; callback inputs multiply quietly and let call sites start depending on return values.

Component frameworks fix the direction of data flow: values arrive from above as inputs and are read-only inside the component. That rule is what makes a component reusable, but it leaves a gap - something happened inside the child, and the data it concerns belongs to somebody else. **Upward signalling** fills that gap, and it comes in two shapes. ## Shape 1: a function passed down as an input The parent creates a function and hands it to the child like any other value. The child keeps no special machinery for it; when the moment arrives it calls it with the facts the parent needs. - **It is an ordinary input.** It is declared, typed and documented with the rest of them, and it can be optional with a no-op default. - **The call is direct and synchronous.** The child's call *is* the parent's handler running. Control returns to the child afterwards, so the child can read a return value - a veto, a normalised value, a promise to await. - **The channel's name is the input's name.** `onRowPick` or `notifySelection` is all the contract there is. - **The child holds a callable.** It knows a function exists; it knows nothing about the parent. ## Shape 2: a declared output event The child declares the set of events it can raise, each with a name and a payload shape. At the usage site the parent binds a handler to the names it cares about. The child emits; the runtime delivers. - **The declaration is public API.** The event list is as much part of the component's contract as its inputs, and it is listable - a reader can see everything the component can tell them. - **Delivery is indirect.** The child names a fact; the runtime finds the handler. Nothing is bound? The emission is simply not observed, and that is legal. - **It is usually fire-and-forget.** There is normally no return value to read, so the child cannot depend on what the parent did. ## How the two compare | Question | Function passed as an input | Declared output event | |---|---|---| | Where the channel is named | in the input list | in the event declaration | | What the child can learn back | the return value | usually nothing | | Is a handler guaranteed | only if the input is required | no - emitting into nothing is legal | | Coupling | the child holds a callable | the child names a fact | | Discoverability | mixed in with data inputs | a separate, listable contract | ## What both refuse to do 1. **Neither lets the child write the owner's state.** The child produces a signal; the write happens in the owner's code. That is the whole point - two writers for one value is how state bugs start. 2. **Neither makes the child the decision-maker.** "The user picked row 7" is a fact. "Open the detail panel" is a decision, and it belongs to the parent, which may do something else entirely on another screen. 3. **Neither guarantees an audience.** A reusable component must behave sensibly when nobody is listening. ## Designing the payload and the name - **Report what happened, not what should happen.** `item-selected` with the item beats `open-detail`; the first survives reuse, the second hard-codes one caller's behaviour into the child. - **Send the minimum the parent cannot derive.** Handing over the child's whole internal state leaks implementation detail that becomes impossible to change later. - **One meaning per channel.** A single `changed` event with a discriminator field inside is a contract every caller has to decode. - **Name consistently.** A past-tense name reads as a notification; an imperative one reads as a command the child is issuing, which inverts the ownership rule. ## What interviewers listen for The answer they want is short - a function called, or a named event emitted - followed by the reason: the child reports, the owner decides, and inputs stay read-only. Candidates who describe the child reaching into the parent, or who cannot say who owns the value after the signal, are describing two-way data flow without knowing it. Being able to say which shape you would pick, and why - a return value needed, versus a contract you want visible in the component's declared API - is what separates a rehearsed answer from an understood one.

  • Why should an upward event carry a fact rather than an instruction?
    Because an instruction bakes one caller's behaviour into the child. `item-selected` can be reused by a screen that filters, one that navigates and one that only logs; `open-detail-panel` forces every caller into the first behaviour or forces the child to be rewritten. Facts keep the decision where the state lives.
  • What does the child gain from the shape where it calls a function directly?
    A return value and ordering. The call runs the parent's code inside the child's own frame, so the child can read what came back - a rejection, a normalised value, something to await - and knows the handler finished before it continues. An emitted event usually gives back nothing.
  • Is it a problem if nothing is bound to a declared output event?
    No. A reusable component must work when a caller ignores an event it does not need, so emitting into nothing has to be legal and silent. What is a problem is a component that only functions correctly if a particular handler exists - then the channel should be a required input, not an optional event.

saying these in an interview costs you the question

  • Says the child updates the parent's state directly when the user acts
  • Emits an event with no payload and expects the parent to guess what happened
  • Treats a declared output name as an internal detail rather than public contract
  • Cannot say who decides what the signal means
  • Thinks a function passed down is special machinery, not an ordinary input
open as a page

Why does an event emitted by a deep child component not reach a distant ancestor the way a host event bubbles?

level: middleimportance: must knowfreq 58%

basics

~10 s

Because a component output resolves at its binding site: emitting invokes the handler bound where that child was used. The component tree carries no propagation path, so nothing continues upward on its own.

open as a page

In a component framework, what does a two-way binding on a child component's value actually expand to?

level: middleimportance: must knowfreq 66%

basics

~20 s

It is sugar for a pair: an input carrying the current value down, plus an output the child raises when it wants a different one, with the write back into the owner's state generated for you.

open as a page

What do you gain by declaring behaviour such as prevent-default or run-once on an event binding rather than in the handler?

level: middleimportance: should knowfreq 42%

basics

~20 s

The declaration is data the framework reads before your code runs, so it can act, skip the call entirely, or register the listener the host needs - and the handler body stays about your domain rather than plumbing.

open as a page

What does a framework gain by handing handlers a normalized wrapper over the host event, and what does it cost?

level: middleimportance: should knowfreq 48%

basics

~20 s

A wrapper gives handlers one uniform event shape across host and browser differences, plus a seam the framework controls. The cost is a second object: whatever it omits needs the underlying event, and its controls act on the framework's layer.

open as a page

Why can a child component not treat a change in the handler it received as a signal that its caller changed something?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Handler identity reflects the runtime's execution model, not the caller's intent. Where the component body re-runs, a fresh function crosses the boundary every update; where the body runs once, one reference is passed for the instance's life.

open as a page