非动态子组件中key prop的工作原理及渲染更新必要性解析
1. key的本质与非动态组件的使用场景
key是React用于标识组件实例的唯一标记,作用不局限于动态列表渲染。默认情况下,React会对同一层级、同类型的组件默认复用已有实例,只有当key发生变化时,才会销毁旧实例、挂载全新的实例。
非动态组件需要使用key的场景主要有三类:
- 需要强制重置组件内部的本地状态
- 同一位置渲染的同类型组件业务上完全无关,不需要复用之前的实例状态
- 使用非受控组件时,数据源发生切换需要重置默认值
2. 你的问题根因分析
你遇到的「props更新但组件未渲染新值」的问题,本质不是组件没有重渲染,而是组件实例被复用,内部状态没有和传入的props同步:
你的Input、Switch组件大概率采用了非受控的实现方式:要么只在初始化时用props设置了内部state,没有写后续props变化的同步逻辑;要么你给组件传的是defaultValue/defaultInput这类默认值属性,而非受控的value属性。非受控组件的值存在DOM或者组件内部状态中,后续props变化不会自动同步更新,自然会展示旧值。
你给B组件加key之后,key变化触发了组件实例的销毁重建,新实例初始化时会用最新的props设置内部状态/默认值,所以展示就正常了。这也是为什么你只传uniqueId作为key不行:如果同一uniqueId对应的条目下value1、value2发生了变化,key没有变,React还是会复用旧实例,内部状态不会更新。
3. 更简洁的实现方案
把所有可变prop都放到key里是非常不推荐的做法,强制重建组件的性能开销远高于普通重渲染,优先推荐以下方案:
最优方案:改成受控组件
给Input、Switch组件传入受控的value/isChecked属性,同时配套onChange回调把值的修改同步回上层的Reducer状态,完全遵循React的单向数据流规则,props变化时组件会自动重渲染更新值,不需要加任何key。
次选方案:同步props到内部状态
如果不想改受控,可以在B组件内部用useEffect监听所有传入的prop变化,prop变化时更新组件内部的状态,也可以实现值的同步,不需要依赖key强制重建。
4. Input组件需要加key的原因
你给B组件加的key没有包含enabled状态的变化,切换enabled时B组件实例没有被重建,内部的Input组件仍然复用旧实例,非受控的值不会自动重置。你给Input加了和enabled绑定的key之后,enabled变化会触发Input实例的重建,自然会用最新的props重置值。
React确实会检测到prop变化触发重渲染,但重渲染只会更新传入组件的props,不会自动修改组件的内部状态,也不会重置非受控组件的DOM值,这就是你需要加key的原因。
内容的提问来源于stack exchange,提问作者NeuralLynx

