React.cloneElement()对虚拟DOM调和的影响及反模式疑问
首先直接给结论:如果像你示例里这样,调用React.cloneElement却没有传递任何新的props(空对象),这确实属于不必要的反模式,你的担心完全正确——这种做法会破坏PureComponent的性能优化效果。
下面拆解具体原因和场景:
为什么会影响PureComponent?
PureComponent的核心是通过shouldComponentUpdate做浅比较来判断是否需要重渲染。当你在父组件的render里调用React.cloneElement(this.props.children, {})时:
- 每次
render都会返回一个新的React元素对象(哪怕传递的是空props,cloneElement也会创建新的引用) - 这个新元素会作为
childrenprops传递给子组件(比如你的SomePureComponent) - 子组件的
shouldComponentUpdate浅比较时,发现props.children的引用变了,就会判定为props更新,触发重渲染——哪怕子组件本身的业务props根本没有变化。
关于虚拟DOM调和机制的澄清
你的理解没有偏差,这里的问题不是调和阶段的组件实例复用(调和会对比元素的type、key来决定是否复用实例),而是PureComponent的props浅比较逻辑被无意义的clone操作干扰了。哪怕调和阶段复用了组件实例,只要shouldComponentUpdate返回true,组件还是会执行重渲染流程。
什么时候React.cloneElement是合理的?
这个API本身不是反模式,它是React中给子组件注入props的常用模式(比如容器组件把内部状态、方法传递给子组件)。比如下面这种场景是完全合理的:
render() { return ( <div> {this.props.children && React.cloneElement(this.props.children, { onSubmit: this.handleSubmit, currentUser: this.state.user })} </div> ) }
但这种场景下要注意:传递的props要尽量稳定,比如用useCallback包裹函数,用useMemo处理对象,避免因为props引用频繁变化导致子组件不必要重渲染。
你的代码该怎么改进?
针对你示例里的情况,直接移除React.cloneElement调用即可,直接渲染原children:
render() { return ( <div> {this.props.children} </div> ) }
这样children的引用会保持和父组件接收的一致,SomePureComponent的浅比较就能正常工作,只有当它自身的props/state真的变化时才会重渲染。
内容的提问来源于stack exchange,提问作者brogrammer

