React中两种动态渲染Counter组件的写法在UI树视角下是否存在差异?
嘿,这个问题问得很到位!其实这两种写法在React的UI树视角下差异还真不小,核心影响的是React会不会复用组件的状态,咱们拆开说:
首先得先搞懂React判断组件状态是否复用的逻辑:React是靠组件在UI树中的位置+组件类型来识别“同一个组件”的——如果位置和类型都没变,它就会复用这个组件的实例,只更新props;如果位置变了,哪怕是同一个类型的组件,它也会当成新的组件实例来挂载,原来的实例会被卸载,状态也会重置。
先看Option 1的写法:
return ( <div> {isPlayerA ? ( <Counter person="Taylor" /> ) : ( <Counter person="Sarah" /> )} </div> );
这种三元表达式的写法,在父组件的<div>下面,始终只有一个Counter组件的“位置”——不管isPlayerA是true还是false,React都会认为这里是同一个位置的同一个类型组件(都是Counter)。所以它不会卸载旧的组件,只会更新person这个prop,组件内部的状态会被完整保留下来。
举个实际的例子:假设Counter内部有个点击累加的count状态,初始值是0。你点击Taylor的Counter加到5,切换到Sarah后,Sarah的Counter会直接显示5(复用了之前的状态),再切回Taylor还是5。
再看Option 2的写法:
return ( <div> {isPlayerA && <Counter person="Taylor" />} {!isPlayerA && <Counter person="Sarah" />} </div> );
这种用两个独立&&表达式的写法,相当于在父组件的<div>下面有两个独立的组件位置:第一个位置对应Taylor的Counter,第二个位置对应Sarah的Counter。React会把这两个当成完全不同的组件位置——当isPlayerA从true变成false时,第一个位置的Counter会被卸载,第二个位置的Counter会被全新挂载;反过来切换时也是一样。
还是用刚才的例子:你点击Taylor的Counter加到5,切换到Sarah后,Sarah的Counter会显示初始值0(新实例);再切回Taylor,Taylor的Counter也会回到0(原来的实例已经被卸载,现在是重新挂载的新实例)。
总结一下核心差异:
- Option 1是同一位置的单个组件实例,React会复用状态,仅更新props;
- Option 2是两个不同位置的组件实例(同一时间仅一个存在),切换时会触发组件的卸载和重新挂载,状态会被重置。
备注:内容来源于stack exchange,提问作者copenndthagen

