skip to content

Inputs & One-Way Flow

How a parent parameterizes a child: declared inputs with required, optional and default values, read-only from the child's side, flowing one way down. Mutating one is the classic mistake.

on this pageshow

questions

5

In a component framework, what are a component's declared inputs, and why must the child treat them as read-only?

level: juniorimportance: must knowfreq 85%

answer

  1. data moves in one direction
  2. the caller owns the value
  3. child reads, derives, never assigns
  4. the next render re-supplies the original
  5. read-only reaches through object references

basics

~20 s

Declared inputs are the named values a parent passes into a child to parameterize it. The parent owns each one and re-supplies it every render, so the child only reads: a write there never reaches the owner.

solid answer

~50 s

A component definition can be instantiated many times; its **declared inputs** are the named slots the caller fills so one definition renders many outputs. Each input has one home — the instance above that supplies it — and the value flows one way, down. The child reads it but must not assign to it: the assignment changes only the child's local binding, the parent never learns of it, and the parent re-supplies the original on its next render, so the change appears to work and then silently reverts. Read-only reaches through references: if the input is an object, the child holds the same object the parent holds, so mutating a field writes into the parent's data behind the framework's back. To change an input, the child reports upward and the owner decides; the new value flows back down.

go deeper

for a junior

Recall the shape: inputs are named values passed in from above, the caller owns them, the child reads them. Never assign to one; report upward instead and let the owner change it.

for a middle

Explain the mechanics: why the write does not reach the caller, why the next render re-supplies the original value, and why read-only extends through an object reference to the data it points at.

for a senior

Show the diagnosis: an input written or mutated inside a child produces an intermittent revert that tracks unrelated re-renders. Be able to say which reactivity models fail loudly and which stay silent.

for a principal

Frame it as a contract rule that keeps a value's writer countable to one. Decide where that rule is enforced — deeply read-only shapes, frozen data, review convention — and what the enforcement costs in ergonomics.

## What a declared input is A component definition is a template for many live instances. A **declared input** is a named slot on that definition that the caller fills in when it renders an instance, which is what lets one definition produce a thousand different outputs. Three ideas travel with every input: - The **name** is part of the component's public contract, like a function parameter name — renaming it breaks callers. - The **value** is supplied by whoever renders the instance, and is re-supplied every time that renderer runs. - The **direction** is fixed: values enter through inputs, and nothing leaves the same way. An input is not the component's own memory. Local state is created inside the instance and survives across updates because the instance owns it. An input is borrowed: the child holds whatever the caller handed it for this update, and nothing more. | | declared input | local state | |---|---|---| | created by | the caller, on each render | the instance, once | | written by | the owner above | the instance itself | | survives an update | only if the caller re-supplies it | yes, the instance keeps it | | good fit for | the caller's configuration and data | the instance's own working value | ## One-way flow "Data down" means a value has one home and one path to every reader. The owner holds it, renders children with it, and those children render their own children with it. When it must change, the change is requested at the owner and re-flows down that same path; the affected part of the tree is re-derived from the new value rather than patched in place at the point of use. The payoff is that "why does the screen show this?" is answerable by reading one chain upward. In a world where any reader may also write, a single value has many writers and the order of their writes is decided by render timing, which nobody controls. ## Why the child may not write to an input 1. **The write does not reach the owner.** Assigning to the slot changes the child's binding, not the expression the parent evaluates when it renders. 2. **The next update overwrites it.** The parent re-supplies the value on its next render, so the child's write survives only until the first unrelated re-render — the classic intermittent bug, where a field "loses" what the user typed as soon as anything else on the screen changes. 3. **It makes the value's home unknowable.** Two writers, no notification between them, no single place to read the truth. Runtimes differ in how loudly this fails. Some make the binding genuinely immutable or warn in development builds; some silently accept the write; in a compile-time runtime the input may compile down to a read of the parent's expression, so the assignment is either meaningless or rejected outright. Because the loudness is not portable, **treat every input as read-only by convention** rather than relying on the runtime to stop you. ## Read-only reaches through the reference When the input is an object, array or function, only the *binding* is read-only. The child holds the same object the parent holds, so writing to a field of it is a write into the parent's data that skips the notification mechanism entirely. The rule has two halves: do not reassign the input, and do not mutate what it points at. Build a replacement value and ask the owner to accept it. ## What an input change actually triggers | reactivity model | how the child learns an input changed | |---|---| | re-runs the component on update | the parent renders, the child's function runs again with the new values | | fine-grained tracking | the specific readers of that input re-run; the component body may not | | compile-time generated updates | generated code assigns the new value and runs only the statements that read it | | periodic dirty checking | the next check compares previous and current values and refreshes the bindings that differ | In all of them the trigger is the *owner* supplying something new — never the child deciding on its own. ## Asking for a change instead of making one The child reports upward: it invokes something the caller supplied, or raises a signal the caller listens for. The caller then changes the value it owns, and the new value flows down. That round trip is the price of one-way flow, and what it buys is one writer per value, so state bugs are localized to an owner you can name. It also makes a child trivial to test: supply inputs, observe output; there is no hidden channel by which the child could have changed its own inputs between two assertions.

  • If the child may not write to an input, how does a user action inside the child ever change that value?
    The child reports the intent upward — it invokes something the caller supplied, or raises a signal the caller listens for — and the caller changes the value it owns. The new value then flows back down as an input on the next render. The child stays a pure function of what it was given.
  • Why is a child that only reads its inputs so much easier to test than one that writes to them?
    Its output is a function of the inputs supplied plus its own state, so a test can set inputs and assert output with no hidden channel in between. A child that writes to its inputs can change the caller's data mid-assertion, so results depend on render order rather than on the values under test.
  • Does declaring an input read-only in a type system make it safe to pass a mutable object down?
    No. A shallow read-only declaration stops reassignment of the slot, not writes into the object the slot points at, and it only constrains callers that are compiled against it. Safety comes from a deeply read-only shape, from freezing the value, or from the convention that nothing below the owner ever mutates it.

An input is a parameter, not a variable: the caller decides what goes in the envelope, and the callee reading it may not rewrite the letter and expect the sender to know.

saying these in an interview costs you the question

  • Thinks assigning to an input updates the value in the parent
  • Says one-way flow is only a style preference, not a mechanism
  • Assumes mutating a field of an object input is fine since the reference is unchanged
  • Believes the runtime always blocks a write to an input, so nothing can go wrong
  • Treats inputs and local state as interchangeable places to keep a value
  • Expects a write to an input to survive later unrelated re-renders
open as a page

A child mutates a field of an object it received as an input. What breaks, and why do declared types miss it?

level: seniorimportance: must knowfreq 55%

basics

~20 s

The child and the owner hold the same object, so a field write changes the owner's data with no notification, and readers disagree afterwards. Declared types describe shape, not writability, and a check runs when the value arrives, not on every later write.

open as a page

How does a component declare an input as required, optional or defaulted, and where is each rule enforced?

level: middleimportance: should knowfreq 60%

basics

~20 s

An input declaration states the name, whether a value must be supplied, and what stands in when it is absent. Required-ness is checked either at build time or at runtime; a default applies only when the caller omits the input.

open as a page

A value is threaded as an input through five components that only forward it; what does that cost, and how do you choose a remedy?

level: principalimportance: should knowfreq 45%

basics

~20 s

Each forwarding level pays a cost: its contract advertises a value it never uses, and every rename edits every level. The remedies, restructuring the composition, providing the value to a subtree, or keeping it outside the tree, are chosen by reach, lifetime and discoverability.

open as a page

When a template binds a value onto a host element, not a component, how does the framework choose attribute or property?

level: middleimportance: nice to knowfreq 40%

basics

~20 s

One binding syntax covers two targets. On a component it fills a declared input; on a host element the framework chooses between an attribute, which can only hold text, and a property on the live node. Most runtimes decide by recognising the name.

open as a page