React状态保留与重置:为何两个Counter渲染示例的位置判定结果不同?
默认情况下,当组件保持在同一位置时,React会保留该组件的状态。通常这正是你需要的效果,因此该默认行为是合理的。
示例取自新版React官方文档。
核心判定规则前置说明
React所说的「组件位置」,指的是虚拟DOM树中的节点层级、顺序索引,和浏览器渲染后的页面视觉位置无关,也和单次渲染是否仅出现一个同类型组件无关。判断组件是否复用实例的核心依据是:虚拟DOM位置 + 组件类型,两者完全一致才会复用原有实例、保留状态。
示例1 - 相同位置
核心渲染逻辑:
return ( <div> {isPlayerA ? ( <Counter person="Taylor" /> ) : ( <Counter person="Sarah" /> )} <button onClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> );
该写法中,两个Counter都在同一个三元表达式的返回结果里,对应div父节点下的第一个子节点槽位。isPlayerA切换时,只是这个槽位里的Counter的personprop发生了变化,React判定为同一位置的同一类型组件,会复用原有Counter实例,保留score等状态。
示例2 - 不同位置
核心渲染逻辑:
return ( <div> {isPlayerA && <Counter person="Taylor" /> } {!isPlayerA && <Counter person="Sarah" /> } <button onClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> );
该写法中,两个Counter分别属于两个独立的条件渲染语句,对应div父节点下的两个不同顺序的子节点槽位:第一个槽位对应isPlayerA && ...的返回值,第二个槽位对应!isPlayerA && ...的返回值。
当isPlayerA切换为false时,第一个槽位的Counter会被销毁;第二个槽位原本是空值,现在会新建一个Counter实例。React判定两个Counter属于不同位置的组件,不会复用实例,原有状态会被重置。
你遗漏的核心规则要点
- 虚拟DOM的子节点是按顺序逐个比对的,每个顺序位置对应独立的槽位,哪怕某个槽位渲染结果是空值,槽位本身也依然存在
- 多个独立的条件渲染语句,会占据父组件下多个不同的顺序槽位,互相独立
- 组件状态是绑定在「虚拟DOM槽位 + 组件类型」对应的实例上的,槽位变化就会触发实例销毁重建,状态自然不会保留
内容的提问来源于stack exchange,提问作者Noob
相关产品推荐
相关产品推荐

