React调和机制异常:组件状态为何重置情况不一致?
React组件位置变化时的状态保留差异问题
我在React官方文档中看到一个重置状态的示例,为理解该示例,将其简化为多个实验,但无法解释观察到的结果。
所有实验均使用同一版本的Counter组件:
function Counter() { const [score, setScore] = useState(0); useEffect(() => { const i = setInterval(() => setScore((n) => n + 1), 200); return () => clearInterval(i); }, []); return <div>{score}</div>; }
Counter挂载后会快速计数,卸载再挂载时状态会重置为0。
实验一:Counter包裹在有状态容器中
export default function Scoreboard() { const [bool, setBool] = useState(true); return ( <div> {bool && <Counter />} {!bool && <Counter />} <button onClick={() => { setBool(!bool); }} > Switch </button> </div> ); }
点击“Switch”时,Counter状态会重置。
实验二:重构后的直观版本
export default function Scoreboard() { const [bool, setBool] = useState(false); return ( <div> {bool ? ( <> <div /> <Counter /> </> ) : ( <> <Counter /> <div /> </> )} <button onClick={() => { setBool(!bool); }} > Switch </button> </div> ); }
点击“Switch”时,Counter状态依然会重置。
基于前两个实验,我初步认为:组件位置变化时状态不会保留。但日常开发中,动态列表后跟随有状态元素并未出现此情况,于是做了第三个实验。
实验三:动态列表后跟随Counter
export default function Scoreboard() { const [count, setCount] = useState(0); return ( <div> <> {Array(count) .fill(0) .map(() => ( <div>a</div> ))} <Counter /> </> <button onClick={() => { setCount((n) => n + 1); }} > Switch </button> </div> ); }
令人惊讶的是,每次点击“Switch”,Counter位置变化,但状态从未重置。
问题
为何前两个示例中状态未保留,第三个却保留?所有案例中仅Counter相对兄弟元素的位置发生变化。
补充说明:所有案例均未指定key,默认使用索引。不同位置调用JSX元素不会赋予不同标识,否则第一个示例中状态不会重置。我知道添加key可改变此行为,但想理解这种行为差异的根本原因。
内容的提问来源于stack exchange,提问作者Slava Knyazev
相关产品推荐
相关产品推荐

