React将组件作为Props传递时为何不随父组件状态更新重渲染
import * as React from 'react' import ReactDOM from 'react-dom' function Logger(props) { console.log(`${props.label} rendered`) return null } function Counter(props) { const [count, setCount] = React.useState(0) const increment = () => setCount(c => c + 1) return ( <div> <button onClick={increment}>The count is {count}</button> {props.logger} </div> ) } ReactDOM.render( <Counter logger={<Logger label="counter" />} />, document.getElementById('root'), )
问题解答
核心逻辑是React元素的创建时机直接决定了组件是否会跟随宿主组件重渲染,对应你的三个问题逐一解释:
- 为什么每次更新count状态时,Logger组件不会触发重渲染?
React触发组件重渲染的核心前提是:组件对应的React元素发生变化,或是组件自身状态/消费的Context发生变更。你代码里的<Logger label="counter" />在ReactDOM.render调用时就已经创建完成,相当于提前生成了固定的组件快照传给Counter。当Counter内部count更新触发自身重渲染时,只是复用了之前传入的Logger元素,这个元素本身没有任何变更,所以React不会重新执行Logger的渲染逻辑,自然不会输出打印日志。 - 这种将组件作为props传递的方式是如何避免Logger函数组件的props对象发生变化的?
Logger的props(也就是{label: "counter"})是在创建<Logger />元素时就被固定下来的,这个创建动作发生在Counter组件的外部,和Counter的内部状态完全无关。这段代码中外层只有一次ReactDOM.render调用,没有触发外层重渲染的逻辑,所以Logger的props对象永远不会发生变化,自然不会触发重渲染。 - 为什么Counter组件每次重渲染时,不会创建新的Logger props对象?
核心区别是<Logger />的创建位置:你是把已经创建完成的元素作为props传给Counter,不是在Counter的函数体内部声明<Logger />。如果把<Logger label="counter" />直接写在Counter的return语句中,每次Counter重渲染都会执行一次React.createElement(Logger, {label: "counter"}),生成全新的React元素对象,对应的props对象也会是新的。但现在创建逻辑在Counter外部,Counter重渲染时只会读取传入的props.logger,不会重新执行创建逻辑,自然不会生成新的props对象。
内容的提问来源于stack exchange,提问作者Sean Liu
相关产品推荐
相关产品推荐

