React面试题,封装useEffect

在React中,useEffect 是一个非常常用的钩子,用于处理副作用(如数据获取、订阅或手动更改 DOM)。默认情况下,useEffect 在组件首次渲染时和每次依赖项变化时都会执行。如果你希望封装一个 useEffect,使其在第一次渲染时不执行,可以通过一个自定义钩子来实现。

自定义钩子:useEffectSkipFirst

tsx 复制代码
import { useEffect, useRef } from 'react';

// 自定义钩子,跳过第一次执行,支持清理逻辑
function useEffectSkipFirst(effect: () => void | (() => void), dependencies: any[] = []) {
  const skipFirst = useRef(true); // 使用 useRef 来存储是否跳过第一次的状态

  useEffect(() => {
    if (skipFirst.current) {
      skipFirst.current = false; // 第一次时跳过,并将状态改为 false
      return undefined; // 不执行任何操作
    }

    // 执行传入的副作用函数
    const cleanup = effect();

    // 如果副作用函数返回了一个清理函数,则在组件卸载或依赖变化时执行清理逻辑
    return cleanup;
  }, dependencies); // dependencies 是依赖项数组
}

export default useEffectSkipFirst;

使用方法

tsx 复制代码
import React from 'react';
import useEffectSkipFirst from './useEffectSkipFirst';

const MyComponent = () => {
  useEffectSkipFirst(() => {
    console.log('This effect will not run on the first render');

    // 返回清理逻辑
    return () => {
      console.log('Cleanup logic executed on component unmount or dependency change');
    };
  }, []); // 依赖项为空数组,表示只在依赖项变化时执行

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

export default MyComponent;

工作原理

  1. useRef :useRef 创建一个可变的引用对象,其值在组件的整个生命周期内保持不变。这里用它来存储一个布尔值 skipFirst,初始值为 true。
  2. useEffect :
    • 在 useEffect 中,首先检查 skipFirst.current 的值:
      • 如果是 true,说明是第一次渲染,直接跳过,并将 skipFirst.current 设置为 false。
      • 如果是 false,说明不是第一次渲染,执行传入的副作用函数 effect。
    • 如果 effect 返回了一个清理函数(即返回值是一个函数),则在组件卸载或依赖项变化时执行该清理函数。
  3. 依赖项 :dependencies 数组用于控制 useEffect 的执行时机,与普通 useEffect 的行为一致。

示例解释

  • 首次渲染 :skipFirst.current 为 true,跳过执行 effect,不执行任何操作。
  • 后续依赖变化 :skipFirst.current 为 false,执行 effect,并返回清理函数(如果有)。
  • 组件卸载 :当组件卸载时,useEffect 的返回值(清理函数)会被执行,确保资源被正确清理。

注意事项

  • 确保 effect 函数的返回值是一个函数,否则清理逻辑不会被触发。
  • 如果依赖项数组为空([]),则副作用函数只会在组件首次渲染后和组件卸载时执行。
  • 如果依赖项数组中有值,则副作用函数会在依赖项变化时执行,并在组件卸载时执行清理逻辑。

通过这种方式,你可以实现一个自定义的 useEffect,使其在首次渲染时不执行,但在后续依赖变化时执行,并且在组件卸载时执行清理逻辑。

相关推荐
夏幻灵5 小时前
JavaScript this 面试:五种绑定规则、优先级与常见面试陷阱
开发语言·javascript·面试
Lvan的前端笔记5 小时前
docker:每个前端项目一个 Nginx 容器还是只有一个Nginx容器
前端·nginx·docker
三天不学习5 小时前
Egg.js 4 突然爆火,原因是否归结于AI 原生落地需求爆发?
前端·javascript·全栈·egg.js
xcs194056 小时前
前端 vue 的前端页面debugger 进不去
前端·javascript·vue.js
明月_清风7 小时前
Deno 终局来了:从挑战 Node 到被 Cloudflare 收编
前端·后端·node.js
Csvn8 小时前
框架性能优化
前端
回眸&啤酒鸭10 小时前
【回眸】OpenSwarm 多智能体协作系统实战指南
大数据·前端·人工智能
用户693717500138410 小时前
2026,程序员的时代拐点到了
android·前端·后端
大龄秃头程序员10 小时前
一次 iBeacon + BLE 无感解锁方案的实现记录
前端
小兔子10 小时前
Python 的 GIL 与 free-threading:3.13 之后「去 GIL」走到哪一步了
前端