React组件在父组件中的position计算规则及示例疑问解答
React组件位置与State重置的疑问解析
问题背景
在React的一个示例中,切换isPlayerA状态时,两个Counter组件的State会被重置。示例代码如下:
import { useState } from 'react'; export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return ( <div> {isPlayerA && <Counter person="Taylor" /> } {!isPlayerA && <Counter person="Sarah" /> } <button onClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> ); } function Counter({ person }) { const [score, setScore] = useState(0); const [hover, setHover] = useState(false); let className = 'counter'; if (hover) { className += ' hover'; } return ( <div className={className} onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > <h1>{person}'s score: {score}</h1> <button onClick={() => setScore(score + 1)}> Add one </button> </div> ); }
存在两个疑问:
- 两个
Counter最终渲染后都是父div的第一个子元素,为何文档称它们处于不同位置? - React中组件在父组件内的位置是如何计算的?
核心解析
1. 组件位置的判定逻辑
React判断组件是否为同一实例,依据的是JSX结构中的“逻辑位置”,而非最终渲染后的DOM位置。
在示例的JSX结构里,父div下包含三个逻辑节点:
- 第一个逻辑节点:
{isPlayerA && <Counter person="Taylor" />}(条件渲染表达式) - 第二个逻辑节点:
{!isPlayerA && <Counter person="Sarah" />}(另一条件渲染表达式) - 第三个逻辑节点:
<button>Next player!</button>
当isPlayerA为true时,第一个逻辑节点渲染出Counter,第二个节点返回null;切换为false后,第一个节点返回null,第二个节点渲染出Counter。对React而言,这两个Counter属于不同的逻辑位置节点,因此会被视为完全独立的组件实例,State自然会被重置。
2. DOM位置与逻辑位置的差异
虽然最终DOM中两个Counter都是父div的第一个子元素,但React的组件实例跟踪基于静态JSX结构的节点顺序,而非动态渲染后的DOM结构。React在更新比对时,会按JSX中节点的顺序(逻辑位置)匹配新旧组件。
3. 保留State的修改方案
若想让两个角色共享Counter的State,只需将Counter放在同一个逻辑位置,通过条件切换person属性即可:
export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return ( <div> <Counter person={isPlayerA ? "Taylor" : "Sarah"} /> <button onClick={() => setIsPlayerA(!isPlayerA)}> Next player! </button> </div> ); }
此时Counter始终处于父div的第一个逻辑位置,React会判定为同一组件实例,State将被保留。
内容的提问来源于stack exchange,提问作者Kolyunya
相关产品推荐
相关产品推荐

