Skip to the solution
EasyPhone Screen6 min read

How do you pass data between components in React?

803 views

The problem

Question presented to candidate: "Walk me through the ways data moves between components in React — parent to child, child to parent, and between two siblings."

What a strong answer should cover:

  • Parent to child: props. The default, and read-only.
  • Child to parent: a callback passed down as a prop. Data still flows one way; the child invokes a function it was given.
  • Sibling to sibling: lift state up to the nearest common ancestor and pass it down to both.
  • Deep trees: Context, to avoid prop drilling — but note it is not a state manager, and every consumer re-renders when the value changes.
  • Genuinely global or server state: a store (Redux, Zustand, Jotai) or a data library (TanStack Query, SWR). Server state is a different problem from UI state.
  • Composition (children) as the underrated alternative to Context — often it removes the drilling entirely without any new machinery.
  • The judgement point: choose the narrowest mechanism that works; reaching for Context or Redux too early is the common mistake.
  • Refs and imperative handles exist for the rare escape-hatch case.

Clarifying questions expected:

  • "How far apart are these components in the tree?"
  • "Is this server data or UI state?" — they call for different tools.
  • "How often does it change?" — a frequently-changing Context value is a performance problem.

Code / implementation expected: Yes — lifting state up between two siblings is the canonical thing to write.

  • props
  • data flow
  • component communication
  • context api

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read · 8 sections

Try it in the playground

Open any example in the playground to run and change it.

ReactLifting state up so two siblings stay in sync
import { useState } from "react";

// Sibling A — receives the value. It has no state of its own and no idea
// that another component exists.
function TemperatureInput({ scale, value, onChange }) {
  return (
    <label style={{ display: "block", marginBottom: 10 }}>
      Temperature in {scale === "c" ? "Celsius" : "Fahrenheit"}:{" "}
      <input
        value={value}
        onChange={(e) => onChange(scale, e.target.value)}
        style={{ width: 90 }}
      />
    </label>
  );
}

// Sibling B — reads the same lifted state, derived differently.
function BoilingVerdict({ celsius }) {
  if (Number.isNaN(celsius)) return <p style={{ color: "#888" }}>Enter a number.</p>;
  return <p><strong>{celsius >= 100 ? "The water would boil." : "The water would not boil."}</strong></p>;
}

const toC = (f) => ((f - 32) * 5) / 9;
const toF = (c) => (c * 9) / 5 + 32;
const round = (n) => (Number.isNaN(n) ? "" : String(Math.round(n * 1000) / 1000));

export default function App() {
  // The state lives in the nearest COMMON ANCESTOR of the components that
  // need it. Neither sibling owns it; neither talks to the other.
  const [temperature, setTemperature] = useState("22");
  const [scale, setScale] = useState("c");

  // The single callback both inputs share — this is the "up" direction.
  const handleChange = (whichScale, nextValue) => {
    setScale(whichScale);
    setTemperature(nextValue);
  };

  const parsed = parseFloat(temperature);
  const celsius = scale === "f" ? toC(parsed) : parsed;
  const fahrenheit = scale === "c" ? toF(parsed) : parsed;

  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6, maxWidth: 380 }}>
      <h3>Two inputs, one source of truth</h3>
      <TemperatureInput scale="c" value={scale === "c" ? temperature : round(celsius)} onChange={handleChange} />
      <TemperatureInput scale="f" value={scale === "f" ? temperature : round(fahrenheit)} onChange={handleChange} />
      <BoilingVerdict celsius={celsius} />
      <p style={{ color: "#666", fontSize: 13 }}>
        Type in either box and the other follows. The inputs never communicate —
        they both read from, and write to, the parent that owns the value.
      </p>
    </div>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 11 of 119 in React.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All React.js questions
Up nextEasy

Explain the concept of 'unidirectional data flow' in React.

Continue