React面试题整理

326 阅读3分钟

在 React 中,父组件和子组件的 useEffect 及其清理函数(return)的执行顺序遵循特定的规则,与组件的生命周期阶段(挂载、更新、卸载)密切相关。以下是详细说明:


核心概念澄清

  • useEffect 的 return:是清理函数,用于在组件卸载或依赖项变化时清除副作用(如取消订阅、清理定时器等)。与 render 方法无关
  • render:负责渲染 UI,是同步执行的。父组件的 render 会触发子组件的 render

执行顺序规则

1. 挂载阶段(Mounting)

  • 父组件 render → 父组件 UI 渲染。
  • 子组件 render → 子组件 UI 渲染。
  • 子组件的 useEffect 回调 → 执行子组件的副作用。
  • 父组件的 useEffect 回调 → 执行父组件的副作用。

控制台输出示例

plaintext

复制

Child useEffect - Mounted
Parent useEffect - Mounted

2. 更新阶段(Updating)

  • 父组件 render → 父组件重新渲染。
  • 子组件 render → 子组件重新渲染。
  • 父组件的清理函数(useEffect 的 return  → 清理父组件的旧副作用。
  • 子组件的清理函数(useEffect 的 return  → 清理子组件的旧副作用。
  • 子组件的 useEffect 回调 → 执行子组件的新副作用。
  • 父组件的 useEffect 回调 → 执行父组件的新副作用。

控制台输出示例

plaintext

复制

Parent useEffect - Cleanup (旧依赖)
Child useEffect - Cleanup (旧依赖)
Child useEffect - Updated (新依赖)
Parent useEffect - Updated (新依赖)

3. 卸载阶段(Unmounting)

  • 子组件的清理函数(useEffect 的 return  → 清理子组件的副作用。
  • 父组件的清理函数(useEffect 的 return  → 清理父组件的副作用。

控制台输出示例

plaintext

复制

Child useEffect - Cleanup (卸载)
Parent useEffect - Cleanup (卸载)

为什么是这个顺序?

  1. 挂载阶段

    • React 按照组件树的层级渲染(父 → 子)。
    • 但 useEffect 的回调是异步执行的,且按照子组件 → 父组件的顺序触发,确保子组件的副作用先于父组件完成。
  2. 更新阶段

    • 清理函数(return)的执行顺序与 useEffect 回调相反(父组件 → 子组件),确保依赖项变化时,旧的副作用先被清理。
    • 新的副作用回调仍按子组件 → 父组件顺序执行。
  3. 卸载阶段

    • 子组件作为父组件的一部分,需要先被销毁,因此清理函数按子组件 → 父组件执行。

示例代码验证

jsx

复制

import React, { useEffect, useState } from 'react';

function Parent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('Parent useEffect - Mounted/Updated');
    return () => console.log('Parent useEffect - Cleanup');
  }, [count]);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Update Parent</button>
      <Child />
    </div>
  );
}

function Child() {
  useEffect(() => {
    console.log('Child useEffect - Mounted/Updated');
    return () => console.log('Child useEffect - Cleanup');
  }, []);

  return <div>Child Component</div>;
}

操作步骤及输出:

  1. 首次加载

    plaintext

    复制

    Child useEffect - Mounted/Updated
    Parent useEffect - Mounted/Updated
    
  2. 点击按钮更新父组件

    plaintext

    复制

    Parent useEffect - Cleanup       // 父组件清理旧副作用
    Child useEffect - Cleanup        // 子组件清理旧副作用
    Child useEffect - Mounted/Updated // 子组件执行新副作用
    Parent useEffect - Mounted/Updated // 父组件执行新副作用
    
  3. 卸载父组件

    plaintext

    复制

    Child useEffect - Cleanup        // 子组件清理
    Parent useEffect - Cleanup       // 父组件清理
    

总结

  • 挂载阶段:子组件 useEffect → 父组件 useEffect
  • 更新阶段:父组件清理 → 子组件清理 → 子组件 useEffect → 父组件 useEffect
  • 卸载阶段:子组件清理 → 父组件清理。

理解这些顺序有助于避免副作用交叉污染,确保逻辑正确性和性能优化。