














在 React 15 中,componentWillReceiveProps 和 componentDidUpdate 的多次调用,本质上是同步渲染架构和生命周期设计共同作用的结果。
下面分别解释它们为何会被重复调用。
componentWillReceiveProps 多次调用componentWillReceiveProps(nextProps) 在父组件重新渲染时被调用,无论新 props 是否真的改变(除非子组件实现了 shouldComponentUpdate 并返回 false,但该生命周期依然会被调用)。
因此,只要父组件 render 一次,子组件的 componentWillReceiveProps 就可能被调用一次。
setState 导致多次渲染在 React 15 中,如果父组件在 非 React 合成事件(如 setTimeout、Promise、原生事件)中连续多次调用 setState,每次都会触发一次完整的渲染流程:
// 父组件中
setTimeout(() => {
this.setState({ a: 1 }); // 父组件 render 一次
this.setState({ b: 2 }); // 父组件再 render 一次
}, 0);
父组件每 render 一次,子组件的 componentWillReceiveProps 就会被调用一次。
例如父组件在 componentDidMount 中调用 setState,导致一次额外渲染;如果子组件又在该钩子中改变自身状态,可能引发连锁反应。
componentWillReceiveProps 内部调用 setState这是 React 15 中一个经典“陷阱”。
当你在 componentWillReceiveProps 中调用 this.setState 时,React 会在当前渲染流程结束后立即调度一次新的更新,从而导致:
render + componentDidUpdate);componentWillReceiveProps 可能再次被调用,如果父组件在此期间没有重新渲染,则不会;但子组件的状态更新会触发自身重新渲染,从而再次调用 componentDidUpdate)。这种情况下,componentWillReceiveProps 可能不会自身重复调用,但配合父组件的频繁渲染,依然容易出现多次。
默认情况下,父组件 render 时,子组件的 componentWillReceiveProps 一定会被调用。若父组件本身因状态频繁变化而高频渲染,子组件的该生命周期就会高频触发。
componentDidUpdate 多次调用componentDidUpdate(prevProps, prevState) 在每次渲染完成后被调用。
它的多次调用通常源于多次完整的更新流程,或者在一个更新流程中触发了新的更新。
和上面一样,父组件每完成一次 render,子组件就会完成一次更新(除非 shouldComponentUpdate 阻止),因此 componentDidUpdate 会被调用同样次数。
componentDidUpdate 内部调用 setState如果在 componentDidUpdate 中无条件调用 setState,会形成死循环:
更新 → componentDidUpdate → setState → 再次更新 → 再次 componentDidUpdate → …
React 15 会在几次后给出“超过最大更新深度”的警告,但在此之前 componentDidUpdate 会被反复调用。
即使有条件判断,若条件在每次更新后依然满足,也会造成多次调用。
componentWillReceiveProps 中 setState 引发的二次更新前面提到,子组件在 componentWillReceiveProps 中 setState 会导致当前渲染结束后立即触发第二次更新。
这两次更新都会让子组件执行 render 和 componentDidUpdate,所以你会看到 componentDidUpdate 被调用了两次(一次来自父组件触发,一次来自内部状态变化触发)。
React 15 的渲染是深度优先的。当多个组件相互依赖,在一个更新周期中可能触发多个 setState,导致一次用户操作引发多次完整的渲染树更新,每个组件的 componentDidUpdate 也会相应执行多次。
React.PureComponent:对 props 和 state 进行浅比较,避免因父组件无意义重绘而触发子组件更新。componentWillReceiveProps 中合理比较新旧 props:只有当相关 props 变化时才调用 setState,避免产生额外更新。componentDidUpdate 中无条件调用 setState。setTimeout)中的多个 setState 合并为一个:可以利用 unstable_batchedUpdates(React 15 内部存在,但不暴露给用户,通常无法直接使用),或者将状态聚合后一次性更新。shouldComponentUpdate 手动控制更新时机,减少不必要的渲染。从 React 16.3 开始,componentWillReceiveProps 被标记为不安全,并逐步被 static getDerivedStateFromProps 替代;componentDidUpdate 依然存在,但由于 Fiber 架构引入了可中断渲染和自动批处理,在合成事件和生命周期中的多次 setState 会被合并为一次更新,从而大大减少了不必要的重复调用。
在 React 15 中,componentWillReceiveProps 和 componentDidUpdate 的多次调用,根本原因在于同步递归渲染 + 缺少默认的更新批处理,加上开发者可能在生命周期中不经意地触发额外更新。理解这些机制后,通过优化组件继承方式(PureComponent)、谨慎处理内部 setState 以及合理利用生命周期比较逻辑,可以有效减少多余调用,提升性能。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。