useEffect 钩子详解与实战

简介: 【10月更文挑战第3天】React 作为一个流行的 JavaScript 库,通过 Hooks 大幅简化了组件开发。`useEffect` 是一个核心 Hook,用于处理函数组件中的副作用操作,如数据获取和 DOM 更改。本文详细介绍了 `useEffect` 的基本语法、常见用法及示例,包括模拟 `componentDidMount`、`componentDidUpdate` 和 `componentWillUnmount`。此外,还探讨了如何避免无限循环渲染和内存泄漏等问题,并提供了相应的解决方案,帮助开发者更好地理解和应用 `useEffect`,提升应用程序的性能与稳定性。

在现代前端开发中,React 是一个非常流行的 JavaScript 库,它提供了许多实用的功能来简化组件的开发。其中一个重要的功能就是 Hooks,它允许我们在不编写类组件的情况下使用状态和其他 React 特性。useEffect 是 Hooks 中的一个核心钩子,它可以帮助我们处理副作用(例如数据获取、订阅或手动更改 DOM 等)。本文将详细介绍 useEffect 的用法、常见问题以及如何避免一些常见的错误。
image.png

什么是 useEffect

useEffect 是一个可以在函数组件中执行副作用操作的 Hook。它接收两个参数:一个回调函数和一个依赖数组。回调函数会在渲染后执行,而依赖数组则定义了哪些变量的变化会导致这个副作用重新运行。

基本语法

useEffect(() => {
   
  // 副作用代码
  return () => {
   
    // 清理操作
  };
}, [dependencies]);
  • 回调函数:在这个函数中,你可以执行任何副作用操作。
  • 清理函数:当组件卸载或者副作用重新运行前,这个函数会被调用。
  • 依赖数组:当数组中的值发生变化时,副作用会重新运行。

useEffect 的基本用法

示例 1:模拟 componentDidMount

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

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

  useEffect(() => {
   
    console.log('组件已挂载');
    // 返回一个清理函数
    return () => {
   
      console.log('组件已卸载');
    };
  }, []); // 依赖数组为空,只在组件挂载时运行一次

  return (
    <div>
      <p>当前计数:{
   count}</p>
      <button onClick={
   () => setCount(count + 1)}>增加</button>
    </div>
  );
}

export default Example;

示例 2:模拟 componentDidUpdate

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

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

  useEffect(() => {
   
    console.log('计数更新');
  }, [count]); // 当 count 变化时,副作用会重新运行

  return (
    <div>
      <p>当前计数:{
   count}</p>
      <button onClick={
   () => setCount(count + 1)}>增加</button>
    </div>
  );
}

export default Example;

示例 3:模拟 componentWillUnmount

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

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

  useEffect(() => {
   
    const intervalId = setInterval(() => {
   
      setCount((prevCount) => prevCount + 1);
    }, 1000);

    return () => {
   
      clearInterval(intervalId); // 组件卸载时清除定时器
    };
  }, []);

  return (
    <div>
      <p>当前计数:{
   count}</p>
    </div>
  );
}

export default Example;

常见问题及解决方案

问题 1:无限循环渲染

如果 useEffect 的依赖数组中包含了某个状态值,而这个状态值又在 useEffect 的回调函数中被修改,那么可能会导致无限循环渲染。

示例

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

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

  useEffect(() => {
   
    setCount(count + 1); // 修改 count 导致无限循环
  }, [count]);

  return (
    <div>
      <p>当前计数:{
   count}</p>
    </div>
  );
}

export default Example;

解决方案

为了避免这种情况,可以将状态值从依赖数组中移除,并在回调函数中使用闭包来捕获当前的状态值。

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

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

  useEffect(() => {
   
    function updateCount() {
   
      setCount(count + 1);
    }
    updateCount();
  }, []);

  return (
    <div>
      <p>当前计数:{
   count}</p>
    </div>
  );
}

export default Example;

问题 2:内存泄漏

如果在 useEffect 中创建了一些需要清理的资源(如定时器、网络请求等),但在组件卸载时没有进行清理,就会导致内存泄漏。

示例

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

function Example() {
   
  useEffect(() => {
   
    const intervalId = setInterval(() => {
   
      console.log('每秒更新');
    }, 1000);

    // 没有清理定时器
  }, []);

  return (
    <div>
      <p>当前时间:{
   new Date().toLocaleTimeString()}</p>
    </div>
  );
}

export default Example;

解决方案

useEffect 的回调函数中返回一个清理函数,确保在组件卸载时清理掉这些资源。

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

function Example() {
   
  useEffect(() => {
   
    const intervalId = setInterval(() => {
   
      console.log('每秒更新');
    }, 1000);

    return () => {
   
      clearInterval(intervalId); // 组件卸载时清除定时器
    };
  }, []);

  return (
    <div>
      <p>当前时间:{
   new Date().toLocaleTimeString()}</p>
    </div>
  );
}

export default Example;

总结

useEffect 是 React Hooks 中非常强大的工具,它可以帮助我们处理各种副作用操作。通过合理地设置依赖数组和编写清理函数,我们可以有效地避免一些常见的问题,从而提高应用程序的性能和稳定性。希望本文能帮助你在实际开发中更好地理解和使用 useEffect

目录
相关文章
|
机器学习/深度学习 运维
Moment:又一个开源的时间序列基础模型
MOMENT团队推出Time-series Pile,一个大型公共时间序列数据集,用于预训练首个开源时间序列模型家族。模型基于Transformer,采用遮蔽预训练技术,适用于预测、分类、异常检测和输入任务。研究发现,随机初始化比使用语言模型权重更有效,且直接预训练的模型表现出色。MOMENT改进了Transformer架构,调整了Layer norm并引入关系位置嵌入。模型在长期预测和异常检测中表现优异,但对于数值预测的效果尚不明朗。论文贡献包括开源方法、数据集创建和资源有限情况下的性能评估框架。
989 0
|
JavaScript 前端开发 开发者
React 的正确使用方法:ref 篇
你真的用对了 useRef 吗?在与 TypeScript 一起使用、以及撰写组件库的情况下,你的写法能够避开以下所有场景的坑吗?
|
JSON 监控 关系型数据库
Docker 容器日志分析
Docker 容器日志分析
650 0
项目中使用antd中的upload组件file对象到底是info.file还是info.file.originFileObj_坑
在Ant Design的Upload组件中,`onChange`事件处理函数接收一个对象参数,其中`file`字段在不同情况下可能是一个File对象或包含`originFileObj`属性的对象。为了兼容,可以使用`info.file.originFileObj ? info.file.originFileObj : info.file`来确保获取到原始的File对象。官方建议这种写法,并将在未来的大版本中统一返回包含`originFileObj`属性的对象。
549 1
项目中使用antd中的upload组件file对象到底是info.file还是info.file.originFileObj_坑
|
JavaScript
componentDidUpdate 方法在组件更新后做什么?
【10月更文挑战第27天】在 `componentDidUpdate` 中使用 `this.props` 和 `this.state` 时要小心,因为此时它们已经是更新后的最新值,与 `prevProps` 和 `prevState` 所代表的前一个状态不同。同时,如果在 `componentDidUpdate` 中再次调用 `setState`,要确保不会导致无限循环的更新,通常需要添加适当的条件判断来避免不必要的状态更新。
326 2
|
JavaScript
cnpm 的安装与使用
本文介绍了npm和cnpm的概念、安装nodejs的步骤,以及cnpm的安装和使用方法,提供了通过配置npm使用中国镜像源来加速包下载的替代方案,并说明了如何恢复npm默认仓库地址。
cnpm 的安装与使用
解决ERROR in Conflict: Multiple assets emit different content to the same filename index.html 的问题
解决ERROR in Conflict: Multiple assets emit different content to the same filename index.html 的问题
1197 1
|
XML 监控 Java
异步日志:性能优化的金钥匙
本文主要介绍了Log4j2框架的核心原理、实践应用以及一些实用的小Tips,力图揭示Log4j2这一强大日志记录工具在现代分布式服务架构运维中的关键作用。
|
前端开发 C++
使用 Vite 创建 React+TS+SW 项目并整合 AntDesign 、Scss 等组件或插件
本文记录了如何使用Vite创建一个React+TypeScript+Service Workers(SW)项目,并整合AntDesign组件库和Scss等插件,包括项目的创建、配置问题解决、AntDesign和Scss的整合方法。
928 1
|
前端开发 JavaScript API
React Echarts 使用教程 - 如何在 React 中加入图表(内附数据看板实战搭建案例)
Ehcarts 作为数据展示的组件,应用场景丰富,所以在 React 里引入 Echarts 图表是每个前端必会技能。而 Echarts配置项多且复杂,每个配置项都会细分很多个配置小项,并且还对外暴露了一套 API,包括图表实例,事件监听等,还是有一定的上手难度。本文手把手教大家如何在 React 里使用 Echarts,并结合实际使用场景,分享我是如何处理图表自适应等具体问题。 最后来一个实战教学,教大家如何结合 ant-design React UI 框架,开发企业级的「数字币走势数据看板」,帮助大家加深对 Echarts 的理解。
1178 0