skip to content

When a child component's request needs a value from its parent's response, which part of that waterfall can you remove?

level: middleimportance: must knowfreq 70%

answer

  1. ask where the second input comes from
  2. accidental chains move, real ones do not
  3. rendering as the trigger builds the staircase
  4. flattening can cost extra requests
  5. a real chain needs a contract change

basics

~20 s

Only a request whose input comes from another response has to wait for it. Most nested waits are accidental: the child's key was already available from the URL or a prop, so hoisting the declaration lets both start together.

solid answer

~50 s

First test whether the dependency is real. It is real when the child's request cannot be addressed without a value that only the parent's response contains — an id discovered inside the payload. It is accidental when the child could have asked immediately using something already known: a route parameter, a prop, a value in the URL. Accidental chains are removed by moving the declaration, not by making requests faster: hoist both requests to one place that starts them together, or let the child request by the key it already has. When the dependency is real you have three options — accept the two round trips, change the contract so one response carries what the second call needed, or start a cheap unconditional request early and narrow it afterwards. What never helps is a placeholder: the chain is in the ordering, not in the presentation.

go deeper

for a junior

Learn to spot the shape: a request that starts only when an earlier response ends. Then ask whether the second request really needed something from the first.

for a middle

Explain the difference between a real and an accidental dependency, and show that removing an accidental one means moving where the request is declared, not making it faster.

for a senior

Diagnose from a cold-navigation request timeline, and be ready to argue for a contract change or for taking the dependent region off the critical path.

for a principal

Weigh latency against wasted work and payload growth across the whole surface, and set a rule for which screens are allowed to serialise requests at all.

A **waterfall** is two or more requests that run one after another when the user is waiting for both. The total wait is their sum plus the render work between them. The whole skill here is telling apart a chain the data forces on you from a chain your code's shape caused. ## The test for a real dependency Ask one question: *what is the input to the second request, and where does it come from?* - If the input is **only** present in the first response — an id nested inside the payload, a cursor, a permission flag that decides which endpoint to call — the dependency is **real**. No amount of restructuring on the client removes it. - If the input was already available before the first request finished — a route parameter, a prop passed down, a value in the URL, a selection the user made — the dependency is **accidental**. It exists because of where the request was declared, and moving the declaration removes it. Accidental chains are the common case, and they are easy to miss because the code looks innocent: a parent fetches, renders a child once it has data, and the child fetches using a key the URL had all along. ## Removing an accidental chain 1. **Hoist the declaration.** Declare both requests in one place that exists before either subtree renders — a route-level loader, or a single owner component near the top of the screen — and start them together. Their latencies then overlap and the screen costs the slowest, not the sum. 2. **Request by the key you already have.** If the child needs the record for the id in the URL, it can start that request without waiting for the parent's payload at all. 3. **Stop gating a child's render on a parent's data** when the condition is only about presentation. A child that is rendered only once the parent's data is present cannot start anything until that response lands, even when its own request needed nothing from it. ## Living with a real dependency | Option | What it costs | When it is right | |---|---|---| | Accept the two round trips | one extra full latency on every visit | the second call is rare, or the screen is usable after the first | | Change the contract so one response carries what the second needed | server work; a larger payload for every caller | the pair is always requested together | | Start a broad unconditional request early, refine later | possible wasted work if the refinement narrows it | the early request is cheap and cacheable | | Split the screen so the dependent part is not in the critical path | the dependent region arrives later than the rest | the dependent data is secondary to the screen's purpose | Notice that two of the four are contract or product decisions rather than client techniques. That is the honest answer to "how do I remove this waterfall": if the dependency is real, the client cannot win alone. ## Why nesting is what makes it expensive A chain of independent sibling requests started in the same pass is not a waterfall — those overlap. The cost comes specifically from **rendering as the trigger**: each level of the tree can only ask once the level above it has something to render with. A three-level screen therefore pays three sequential latencies on a cold visit, and the profile has the staircase shape the name comes from. Diagnose it exactly that way: look at the request timeline for a cold navigation and check whether each start time lines up with a previous response's end time. Requests that begin together are fine; a staircase is the defect. ## A trap worth knowing Removing a chain can increase total requests. Hoisting a child's request so it starts immediately means it runs even on visits where the parent's response would have revealed the child was unnecessary — a record the user cannot see, a section that turns out to be empty. That is usually a good trade, because latency is the user's cost and a wasted request is the server's, but it is a trade, and it is the reason a broad early request must be cheap. ## How to answer it Lead with the test — where does the second request's input come from — then split your answer into the accidental case (move the declaration; the requests overlap) and the real case (accept it, change the contract, or take it off the critical path). Say plainly that a nicer loading state does not shorten a chain, and mention that flattening can spend extra requests to buy latency.

  • Why can flattening a waterfall increase the number of requests a screen sends?
    A conditional render suppresses a child's request on visits where the parent's data showed it was unnecessary. Starting that request immediately gives up the suppression, so some visits pay for a response nobody reads. It is usually worth it — latency is the user's cost and the extra call is the server's — but it should be a deliberate trade.
  • Does a warm cache make the distinction between real and accidental dependencies unimportant?
    No. A cached first response can make the second request start sooner, but a real dependency is still sequential whenever the first value is not already present, which includes every cold visit and every change of key. The chain is a property of the data, and caching only sometimes hides it.
  • How do you decide which of several serialised requests to attack first?
    Measure the cold navigation and attack the step that contributes the most wall-clock time before the screen becomes useful, not the slowest request overall. A slow call for a region below the fold matters less than a fast one that gates the headline, so the ordering of the fix follows the user's reading order.

Two errands are only sequential if the second address is written on the first receipt. If you already had both addresses, you queued for nothing.

saying these in an interview costs you the question

  • Treats every nested request as an unavoidable data dependency
  • Claims a better loading placeholder shortens a request chain
  • Says caching removes a cold-visit waterfall on a first visit
  • Calls independent sibling requests started together a waterfall
  • Gates a child's render on a parent's data when its request needed nothing from it
  • Forgets that flattening can issue requests a conditional render would have avoided