You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React如何匹配组件与状态?为何示例2中组件状态会重置?

React状态重置疑问:为何示例2中父组件变化会导致状态丢失?

针对示例1,官方文档提到:

对React而言,重要的是UI树中的位置——而非JSX标记!

App组件返回一个以<Counter />作为第一个子元素的<div>。对React来说,这两个计数器拥有相同的“地址”:根节点的第一个子节点的第一个子节点。这就是React在前后渲染间匹配它们的方式,无论逻辑如何组织。

但在示例2中,为何父组件的变化(从<div>切换为<section>)会导致<Counter>组件的状态被重置?按照上面的说法,它不应该仍然是“根节点的第一个子节点的第一个子节点”这个地址吗?


示例1

import { useState } from 'react';

export default function App() {
  const [isFancy, setIsFancy] = useState(false);
  if (isFancy) {
    return (
      <div>
        <Counter isFancy={true} />
        <label>
          <input
            type="checkbox"
            checked={isFancy}
            onChange={e => {
              setIsFancy(e.target.checked)
            }}
          />
          Use fancy styling
        </label>
      </div>
    );
  }
  return (
    <div>
      <Counter isFancy={false} />
      <label>
        <input
          type="checkbox"
          checked={isFancy}
          onChange={e => {
            setIsFancy(e.target.checked)
          }}
        />
        Use fancy styling
      </label>
    </div>
  );
}

示例2

export default function App() {
  const [isFancy, setIsFancy] = useState(false);
  return (
    <div>
      {isFancy ? (
        <div>
          <Counter isFancy={true} /> 
        </div>
      ) : (
        <section>
          <Counter isFancy={false} />
        </section>
      )}
      <label>
        <input
          type="checkbox"
          checked={isFancy}
          onChange={e => {
            setIsFancy(e.target.checked)
          }}
        />
        Use fancy styling
      </label>
    </div>
  );
}

解答

React匹配组件实例的规则,不止看子节点在父元素中的位置,还会对比父元素的类型:

  • 示例1中,两次渲染时App返回的根元素始终是同一个<div>,它的第一个子节点永远是<Counter>——所以React认为这是同一个组件实例,状态得以保留。
  • 示例2中,根<div>的第一个子节点在切换时,从<div>容器变成了<section>容器。React会把不同类型的父元素视为完全不同的UI分支,旧的<div>(包含内部的<Counter>)会被卸载,新的<section>(包含新的<Counter>)会被重新挂载。组件卸载时状态会被销毁,重新挂载自然就是全新的状态了。

如果要避免这种状态重置,可以给<Counter>添加唯一的key属性,让React通过key来识别这是同一个组件,而非依赖父元素类型和位置:

{isFancy ? (
  <div>
    <Counter key="counter" isFancy={true} /> 
  </div>
) : (
  <section>
    <Counter key="counter" isFancy={false} />
  </section>
)}

内容的提问来源于stack exchange,提问作者MahoDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 09:57:44