首页 / React 19 入门教程 / 状态提升

React 19 入门教程

状态提升

本教程共 50 篇 · 第 17 篇 · 更新于 2026-07-29 · 约 7 分钟阅读

React状态提升props组件通信兄弟组件

17. 状态提升

本节目标:掌握”状态提升”模式,让多个组件共享同一份数据,实现兄弟组件之间的通信。

什么时候需要状态提升

两个兄弟组件需要同步数据时,把状态提到它们最近的公共父组件:

        App(状态在这里)
       /  \
   Panel1  Panel2(都读到同一份状态)

一个温度转换的例子

摄氏度和华氏度输入框,需要实时同步:

function TemperatureConverter() {
  const [celsius, setCelsius] = useState('');
  const [fahrenheit, setFahrenheit] = useState('');

  return (
    <>
      <CelsiusInput value={celsius} onChange={setCelsius} />
      <FahrenheitInput value={fahrenheit} onChange={setFahrenheit} />
    </>
  );
}

但这样两个状态是独立的,改了摄氏度,华氏度不会跟着变。

提升状态到公共祖先

把温度值统一用摄氏度表示,提到父组件:

function TemperatureConverter() {
  const [temperature, setTemperature] = useState('');
  const [scale, setScale] = useState('c');

  const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
  const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

  function handleCelsiusChange(value) {
    setTemperature(value);
    setScale('c');
  }

  function handleFahrenheitChange(value) {
    setTemperature(value);
    setScale('f');
  }

  return (
    <>
      <CelsiusInput value={celsius} onChange={handleCelsiusChange} />
      <FahrenheitInput value={fahrenheit} onChange={handleFahrenheitChange} />
    </>
  );
}

现在两个输入框共享同一份状态,改了任意一个,另一个自动更新。

子组件只负责展示和通知

子组件不持有状态,只通过 props 接收值和回调:

function CelsiusInput({ value, onChange }) {
  return (
    <fieldset>
      <legend>摄氏度:</legend>
      <input
        value={value}
        onChange={e => onChange(e.target.value)}
      />
    </fieldset>
  );
}

受控组件模式

子组件的值完全由父组件控制,这就是”受控组件”。下一章会详细讲。

数据流是单向的

父组件状态
    ↓ props(数据向下)
子组件渲染
    ↓ 用户输入
子组件调用回调
    ↑ 事件向上
父组件更新状态

重新渲染,新数据再次向下流

这就是 React 的单向数据流。数据只能从父到子,事件只能从子到父。

单向数据流的好处

数据流向清晰,出了问题好排查。你知道数据一定来自父组件,不会从某个角落冒出来。

一个更实际的例子:搜索筛选

function ProductList({ products }) {
  const [query, setQuery] = useState('');

  const filtered = products.filter(p =>
    p.name.toLowerCase().includes(query.toLowerCase())
  );

  return (
    <>
      <SearchBar value={query} onChange={setQuery} />
      <ProductTable products={filtered} />
    </>
  );
}

function SearchBar({ value, onChange }) {
  return (
    <input
      placeholder="搜索商品..."
      value={value}
      onChange={e => onChange(e.target.value)}
    />
  );
}

function ProductTable({ products }) {
  return (
    <ul>
      {products.map(p => <li key={p.id}>{p.name}</li>)}
    </ul>
  );
}

搜索框和商品列表共享 query 状态,但状态只存在于父组件。

什么时候不该提升

不是所有状态都需要提升。如果一个状态只在一个组件内部用,就留在那里:

  • 表单输入的临时值 → 留在表单组件
  • 弹窗的开关 → 留在弹窗组件
  • 多个组件都要读的数据 → 提升到公共祖先

不要过度提升

状态提得太高,props 链会很长。中间组件被迫传递它不关心的 props。后面会讲 Context 解决这个问题。

本节要点

  • 兄弟组件共享状态 → 提升到公共父组件
  • 数据通过 props 向下传,事件通过回调向上传
  • 子组件可以是纯受控的,不持有状态
  • 单向数据流让状态变化可预测