SyntaxStudy
Sign Up
React Beginner 1 min read

Lifting State Up

When two sibling components need to share the same piece of data, the recommended solution is to "lift state up" — move the state to their closest common ancestor and pass it down as props. The ancestor owns and manages the state; siblings receive it as props and receive setter callbacks to request changes. This enforces unidirectional data flow and makes the source of truth explicit and easy to trace. Lifting state is a deliberate architectural decision. Over-lifting — moving every piece of state to the root component — causes excessive prop drilling and makes the component tree hard to follow. The principle is to keep state as close to where it is used as possible, lifting only when sharing is necessary. React Context and third-party state managers like Zustand or Redux solve the problem at a larger scale, but lifting state directly is the correct first step for many patterns. A derived value — one that can be calculated from existing state or props — should not be stored in state at all. Storing redundant derived state causes synchronisation bugs when one piece is updated but the derived copy is not. Compute derived values inline during render instead. This guideline pairs naturally with lifting state: the canonical state lives in one place and derived views are computed from it.
Example
import { useState } from 'react';

// State lifted to the common parent (TemperatureApp)
function CelsiusInput({ value, onChange }) {
  return (
    <label>
      Celsius:{' '}
      <input
        type="number"
        value={value}
        onChange={e => onChange(Number(e.target.value))}
      />
    </label>
  );
}

function FahrenheitInput({ value, onChange }) {
  return (
    <label>
      Fahrenheit:{' '}
      <input
        type="number"
        value={value}
        onChange={e => onChange((Number(e.target.value) - 32) * 5 / 9)}
      />
    </label>
  );
}

function TemperatureApp() {
  // Single source of truth: temperature in Celsius
  const [celsius, setCelsius] = useState(0);
  const fahrenheit = celsius * 9 / 5 + 32; // derived — not stored in state

  return (
    <div>
      <h2>Temperature Converter</h2>
      <CelsiusInput value={celsius} onChange={setCelsius} />
      <br />
      <FahrenheitInput value={fahrenheit} onChange={setCelsius} />
      <p>{celsius.toFixed(1)} °C = {fahrenheit.toFixed(1)} °F</p>
    </div>
  );
}

export default TemperatureApp;