为什么 React 的函数组件每次渲染执行两次

news2024/11/13 11:05:45

1.这是 React18 才新增的特性。

2.仅在开发模式("development")下,且使用了严格模式("Strict Mode")下会触发。 生产环境("production")模式下和原来一样,仅执行一次。

在 React 中,当你看到某些代码执行了多次(例如,console.log 输出了两次),这通常是由几个常见原因导致的:

  1. 严格模式(Strict Mode):
    在开发模式下,React 的严格模式会故意使组件重新渲染两次,以便更容易发现潜在的副作用和其他问题。你可以检查你的 index.js 或者 main.js 文件,看看是否有 <React.StrictMode> 包裹了你的应用。
  2. 双重渲染:
    React 可能会因为某些状态变化或者父组件的重新渲染导致子组件重新渲染多次。
  3. 热模块替换(Hot Module Replacement, HMR):
    在开发过程中,HMR 可能会导致组件重新加载,尤其是在使用一些开发工具和环境时。
  4. 还有一个原因是检查组件是否为纯函数

解决方法

为了检查和解决这个问题,你可以尝试以下步骤:

1. 检查是否启用了严格模式

在 index.js 或 main.js 中,如果你看到如下代码,可以注释掉 <React.StrictMode> 并观察行为变化:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
// <React.StrictMode>
<App />,
// </React.StrictMode>,
document.getElementById('root')
);
2. 添加唯一的 key 属性:(切记,很重要!)

如果你在列表中渲染元素,确保每个元素都有唯一的 key 属性。这有助于 React 更好地管理列表项。

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

const App = () => {
  const [listData, setListData] = useState([]);

  useEffect(() => {
    // 初始化加载数据
    const initialData = Array.from({ length: 5 }, (_, i) => `Item ${i + 1}`);
    setListData(initialData);
  }, []);

  return (
      <div className="content">
        {listData.map((item, index) => (
          <div key={index} className="list-item">
            {item}
          </div>
        ))}
      </div>
  );
};

export default App;
3. 使用 useEffect 的依赖项:(最容易导致。。。。。。)

检查 useEffect 是否有明确的依赖项。比如:

useEffect(() => {
 console.log('handleLoadMore');
 handleLoadMore();
}, [依赖项数组]);
  • 第一个参数是一个回调函数,其中包含副作用代码。
  • 第二个参数是依赖项数组,控制副作用的执行时机

 当依赖项数组为空时,useEffect 仅在组件首次渲染后执行一次。之后即使组件重新渲染(比如因为状态或属性变化),该副作用也不会再次执行。

useEffect(() => {
  // 依赖项变化时执行副作用
}, [dep1, dep2]);

当依赖项数组包含特定变量时,useEffect 会在组件首次渲染后执行,并在任何一个依赖项发生变化时重新执行。

useEffect(() => {
  // 每次渲染后执行副作用
});

如果没有提供依赖项数组,useEffect 会在每次组件渲染后执行(记得设置一个空数组[])。这通常会导致性能问题,除非你的副作用非常轻量且必须每次渲染后都执行。

useEffect(() => {
  const handle = setInterval(() => {
    console.log('Interval running');
  }, 1000);

  return () => {
    clearInterval(handle);
  };
}, []);

有时候需要在组件卸载或副作用重新运行之前进行清理。例如,取消订阅或清除计时器。

开发者在编写代码时应该遵循React的最佳实践,比如:

  • 使用useStateuseEffect等Hooks时,明确依赖项数组。确保正确地列出所有外部依赖,避免不必要的副作用执行。
  • 尽可能地将计算移动到渲染之外。例如,如果有重复的计算逻辑,可以考虑使用useMemouseCallback来优化。
  • 在开发过程中利用React开发者工具。这些工具能够帮助你更好地理解组件的渲染行为和性能问题。
4. 排除 HMR 的影响

确保热模块替换没有干扰到你的调试。你可以在开发模式下尝试禁用 HMR。

(1)Create React App:

在你的 .env 文件中添加:

FAST_REFRESH=false

如果没有 .env 文件,可以在启动命令前添加环境变量:

FAST_REFRESH=false npm start

2)Webpack:

Create React App 默认隐藏了 Webpack 配置,所以你需要使用 react-app-rewired 或 craco 来覆盖配置。以下是使用 craco 的示例:

npm install @craco/craco
创建一个 craco.config.js 文件,禁用 HMR:
module.exports = {
  webpack: {
    configure: (webpackConfig, { env, paths }) => {
      if (env === 'development') {
        webpackConfig.devServer = {
          ...webpackConfig.devServer,
          hot: false,
        };
      }
      return webpackConfig;
    },
  },
};

修改 package.json 的启动脚本:

"scripts": {
  "start": "craco start",
  "build": "craco build",
  "test": "craco test",
  "eject": "react-scripts eject"
}

(3)自定义的 Webpack:

如果你有自定义的 Webpack 配置,可以直接在 webpack.config.js 中禁用 HMR:

module.exports = {
  // ...其他配置
  devServer: {
    hot: false,
  },
};

(4)Vite:

如果你使用 Vite 作为构建工具,可以通过修改 Vite 配置来禁用 HMR:

export default {
  server: {
    hmr: false,
  },
};

(5)Next.js:

Next.js 使用其自己的 Webpack 配置,禁用 HMR 可能需要一些额外步骤:

自定义 next.config.js:

module.exports = {
  webpackDevMiddleware: config => {
    config.watchOptions = {
      poll: 300,
      aggregateTimeout: 300,
      ignored: /node_modules/,
    };
    return config;
  },
};
5. 调试工具

使用 Facebook 的 React DevTools 调试工具,观察组件的渲染情况和状态变化,找到导致重复渲染的具体原因。

相关问答FAQs:

1. React 的函数组件在每次渲染时会执行两次的原因是什么?

React 的函数组件在每次渲染时执行两次的原因是 React 使用了一种称为 "Double Render" 的机制。这意味着 React 在首次渲染组件时会执行一次函数,然后比较前后两次的函数执行结果,如果发现有变化,就会触发一次重新渲染来更新页面。因此,第一次执行函数用于比较前后结果是否有变化,第二次执行函数则是用于实际渲染页面。这种机制可以确保组件在更新时只进行必要的渲染,提高性能和效率。

2. React 函数组件为什么会在渲染时执行两次而不是一次?

React 函数组件在渲染时执行两次的原因是为了支持 Hooks 机制。Hooks 是 React 16.8 版本引入的新特性,它允许我们在函数组件中使用状态和其他 React 特性,以及实现更复杂的逻辑。为了使 Hooks 正常工作,React 需要执行两次渲染,第一次以收集 Hooks 的信息,第二次以执行每个 Hook 的副作用和渲染逻辑。

3. React 函数组件为什么会执行两次而不是一次呢?有什么好处?

React 函数组件执行两次渲染的好处是可以保证副作用的正确执行和避免出现意外的副作用。副作用指的是在组件渲染过程中可能产生的对外部环境的修改,比如网络请求、订阅事件等操作。通过执行两次渲染,React 可以在第一次渲染时收集副作用信息,然后在第二次渲染时执行这些副作用,确保它们按照正确的顺序和时机执行。

这种机制可以避免副作用导致的错误,并且使得组件的逻辑更加清晰和可控。方便开发者编写高质量的代码,并且提供更好的性能。所以,尽管函数组件执行两次渲染可能会带来一些性能开销,但它带来的好处和灵活性远远超过了这些开销。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2044040.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

整合Rocketmq实现审批流消息推送

文章目录 Docker 部署 RocketMQ拉取 RocketMQ 镜像创建容器共享网络 部署NameServer创建目录并授予权限拷贝启动脚本启动容器NameServer 部署Broker Proxy创建挂载文件夹并授权创建broker.cnf文件拷贝启动脚本启动容器Broker 部署RocketMQ控制台&#xff08;rocketmq-dashboar…

12、springboot3 vue3开发平台-前端-记住我功能实现

文章目录 1. 前端用户信息保存2. 登录页面添加3. 后端实现 1. 前端用户信息保存 使用pinia持久化保存用户名密码 src/stores/remember-me.js // 定义 store import { defineStore } from "pinia" import {reactive} from vueexport const useRememberMeStore defi…

求职Leetcode算法题(7)

1.搜索旋转排序数组 这道题要求时间复杂度为o&#xff08;log n&#xff09;&#xff0c;那么第一时间想到的就是二分法&#xff0c;二分法有个前提条件是在有序数组下&#xff0c;我们发现在这个数组中存在两部分是有序的&#xff0c;所以我们只需要对前半部分和后半部分分别…

element ——tree组件懒加载数据、自定义label、修改高亮样式、回显点击状态

需求 整体宽高占一屏&#xff0c;超出滚动条tree组件点击懒加载每一级数据&#xff0c;一共三级三级节点前加icon&#xff0c;标识是否已学习点击高亮显示背景图横向超出省略显示或者横向滚动条纵向超出纵向滚动条修改其字体和间距☆☆☆☆☆从别的页面跳入回显三级点击状态 …

netsh int tcp show global查看TCP参数

TCP 全局参数 接收方缩放状态 : enabled 接收窗口自动调节级别 : normal 加载项拥塞控制提供程序 : default ECN 功能 : disabled RFC 1323 时间戳 : allowed 初始 RTO : 1000 接收段合并状态 : enabled 非 Sack Rtt 复原 : disabled 最大 SYN 重新传输次数 : 4 快速打开 : en…

CrowdTransfer:在AIoT社区中实现众包知识迁移

这篇论文的标题是《CrowdTransfer: Enabling Crowd Knowledge Transfer in AIoT Community》&#xff0c;由 Yan Liu, Bin Guo, Nuo Li, Yasan Ding, Zhouyangzi Zhang, 和 Zhiwen Yu 等作者共同撰写&#xff0c;发表在《IEEE Communications Surveys & Tutorials》上。以下…

springboot航班进出港管理系统--论文源码调试讲解

第2章 开发环境与技术 本章节对开发航班进出港管理系统管理系统需要搭建的开发环境&#xff0c;还有航班进出港管理系统管理系统开发中使用的编程技术等进行阐述。 2.1 Java语言 Java语言是当今为止依然在编程语言行业具有生命力的常青树之一。Java语言最原始的诞生&#xff…

网络协议--TCP/IP协议栈--三握和四挥

文章目录 网络设备交换机交换机的工作原理 路由器路由器工作原理 TCP/IP协议栈TCP/IP四层模型TCP/IP通信过程TCP特性TCP包头结构源端口、目标端口序列号(seq)确认号(小ack)标记位 TCP协议端口号端口号分类ssh服务nc工具抓包 socket套接字端口占用 三次握手Wireshark抓包tcpdump…

构建完美人工智能工程师培养计划

一、理论基础构建 1. 数学与统计学基础&#xff1a;作为AI的基石&#xff0c;扎实的数学与统计学知识不可或缺。培养计划应涵盖高等数学、线性代数、概率论与数理统计、优化理论等课程&#xff0c;为学员打下坚实的理论基础。 2. 计算机科学基础&#xff1a;包括数据结构、算…

DLT645-2007通信协议---读取解析智能电表数据

一、DLT645-2007通讯协议 DLT645-2007是中国电力行业规定的一种智能电表通信协议&#xff0c;主要用于电能表与数据采集设备之间的通信。DLT645-2007协议定义了电能表与数据采集设备之间的数据格式、通信方式、命令集等内容&#xff0c;用于实现电能表数据的采集、传输和管理。…

SpringBoot整合Liquibase

1、是什么&#xff1f; Liquibase官网 Liquibase是一个开源的数据库管理工具&#xff0c;可以帮助开发人员管理和跟踪数据库变更。它可以与各种关系型数据库和NoSQL数据库一起使用&#xff0c;并提供多种数据库任务自动化功能&#xff0c;例如数据库迁移、版本控制和监控。Li…

盲盒抽奖源码

介绍&#xff1a; 功能上还可以,商品和盲盒可以在你程序里添加&#xff0c;设置概率等!! 新盲盒星球抽奖商城手机网站源码 随机开箱抢购 代码有点大&#xff0c;三百多M。 教程搭建很简单&#xff0c;基本10分钟搭建一套&#xff0c;可一个服务器搭建多套&#xff0c;只要你…

【时时三省】(C语言基础)模拟实现字符串相关函数

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 模拟实现库函数:strcpy 示例: const修饰指针 示例: const 修饰变量&#xff0c;这个变量为常变量&#xff0c;不能被修改&#xff0c;但本质上还是变量 正常num&#xff1d;20是改不了它…

招聘管理型岗位,HR会考察候选人的哪些方面?

团队管理能力 团队管理能力可以说是管理型岗位最基本的要求&#xff0c;只有具备优秀的团队管理能力&#xff0c;才能够带领团队实现组织目标&#xff0c;提高团队凝聚力&#xff0c;而想要考察一个人是否具备团队管理能力&#xff0c;就要通过多方面来测试。可以先了解一下候…

CSS笔记总结:第五天(HTML+CSS笔记完结)

Xmind鸟瞰图&#xff1a; 简单文字总结&#xff1a; css知识总结&#xff1a; 元素的显示与隐藏&#xff1a; 1.通过display隐藏元素 不保留位置 2.通过visibility 隐藏元素 保留位置 3.overflow 溢出隐藏 鼠标样式cursor&#xff1a; 1.defauly小白 2.p…

走进 keepalived:解析高可用架构背后的关键技术

一、什么是keepalived Keepalived 是一个用于实现服务器高可用性&#xff08;High Availability&#xff0c;简称 HA&#xff09;的软件。 简单来说&#xff0c;它的主要作用是检测服务器的状态&#xff0c;并在主服务器出现故障时&#xff0c;自动将服务切换到备份服务器上&…

SVN权限控制解析

一、基础数据说明 1. 代码目录存在多级 2. 角色存在多级 二、规则说明 结合例子讲规则 1、多级文件夹 a. 继承与覆盖 【文件夹层级】&#xff1a; Repositories/BS_Projects/科顺 BS_Projects包含了多个项目&#xff0c;每个项目是一个文件夹&#xff0c;比如“科顺”是其…

分布式事务Seata保证审批状态一致性

文章目录 下载安装Seata创建对应数据库修改application.yml相应配置启动SeataPmHub 实战——添加任务事务管理业务库添加undo_log 表对应服务加上对应的seata依赖Nacos 配置文件 pmhub-project-dev.yml 添加 seata 配置&#xff1a;接口添加 GlobalTransactional 注解涉及数据表…

Centos7升级gitlab(17)

在 CentOS 7 中将 GitLab 从版本 17.1.1 升级到 17.2.2&#xff0c;涉及以下步骤。请务必在升级前备份数据&#xff0c;以防止升级过程中出现问题导致数据丢失。 升级步骤 1. 备份 GitLab 数据 在升级之前&#xff0c;确保已经备份了 GitLab 的数据&#xff0c;包括数据库、…

【windows安装gradle】

1.去官网下载自己需要的版本。 2.直接解压到指定目录 3.配置环境变量 3.1.新建 GRADLE_HOME 环境变量值指向你的 Gradle 的解压路径 3.2.将 %GRADLE_HOME%\bin 添加到 Path 环境变量中 4.打开cmd命令行输入gradle -v查看是否安装成功以及当前版本 下面显示说明已经安装完成了…