0

0

React 函数组件中DOM操作与副作用管理:构建可靠日历组件的实践

碧海醫心

碧海醫心

发布时间:2025-07-11 23:22:01

|

282人浏览过

|

来源于php中文网

原创

React 函数组件中DOM操作与副作用管理:构建可靠日历组件的实践

本文旨在解决React函数组件中不当的DOM操作和副作用管理导致的组件初始化问题。通过深入探讨useEffect、useState和useCallback等React Hooks,我们将展示如何正确地在组件挂载和更新时执行逻辑,避免直接操作DOM的常见陷阱,并构建一个稳定、可预测的日历组件。文章将提供详细的代码示例和最佳实践建议,帮助开发者理解React的声明式UI范式,从而编写出更健壮、性能更优的React应用。

1. 理解React中的DOM操作误区

在react中,组件的渲染是声明式的:你描述ui在给定状态下应该是什么样子,react会负责高效地更新dom以匹配这个描述。直接使用document.queryselector、innertext或innerhtml来操作dom元素,尤其是在函数组件的顶层(即组件函数体内部,return语句之前),是与react的声明式范式相悖的。

原始代码中存在以下问题:

  • DOM元素未就绪: 在组件函数顶层执行document.querySelector时,对应的DOM元素可能尚未被React渲染到页面上,导致获取不到元素或获取到旧的引用。这会使得renderCalendar()在首次运行时失败或行为异常。
  • 副作用管理不当: renderCalendar()函数包含了对DOM的直接修改,这是一种副作用。在React函数组件中,副作用应该通过useEffect钩子来管理,以确保它们在正确的生命周期阶段执行,并且能够被React有效地控制。
  • 重复执行与性能问题: 将renderCalendar()直接放在组件函数体中(return之前),会导致每次组件渲染时都重新执行该函数,即使DOM元素和数据没有变化。这不仅是性能浪费,还可能导致不可预测的行为,例如事件监听器被重复添加。

2. 使用useState管理组件状态

在React中,任何会影响UI渲染的数据都应该通过状态(State)来管理。对于日历组件,当前显示的年份和月份是核心状态。

import React, { useEffect, useState, useCallback } from 'react';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';

export const Calendar = () => {
  // 使用useState管理当前显示的年份和月份
  const [currYear, setCurrYear] = useState(new Date().getFullYear());
  const [currMonth, setCurrMonth] = useState(new Date().getMonth());
  // 存储日历日期部分的HTML字符串,后续将通过dangerouslySetInnerHTML渲染
  const [calendarDaysHtml, setCalendarDaysHtml] = useState('');

  const months = ["January", "February", "March", "April",
                  "May", "June","July", "August", "September",
                  "October", "November", "December"];

  // ... 后续代码
};

通过useState,当currYear或currMonth更新时,React会自动触发组件的重新渲染,从而反映最新的日期。

3. 使用useCallback优化函数引用

renderCalendar函数依赖于currYear和currMonth这两个状态变量。如果我们将renderCalendar作为useEffect的依赖项,那么每次组件渲染时,如果renderCalendar的引用发生变化,useEffect都会重新执行。为了避免不必要的重新创建函数和useEffect的重新触发,我们可以使用useCallback钩子来缓存函数实例。

幻舟AI
幻舟AI

专为短片创作者打造的AI创作平台

下载
// ... (之前的import和useState定义)

export const Calendar = () => {
  // ... (useState定义)

  // 使用useCallback包装renderCalendar,确保其引用在依赖项不变时保持稳定
  const renderCalendar = useCallback(() => {
    let firstDayofMonth = new Date(currYear, currMonth, 1).getDay();
    let lastDateofMonth = new Date(currYear, currMonth + 1, 0).getDate();
    let lastDayofMonth = new Date(currYear, currMonth, lastDateofMonth).getDay();
    let lastDateofLastMonth = new Date(currYear, currMonth, 0).getDate();
    let liTag= "";

    for (let i = firstDayofMonth; i > 0; i--) {
      liTag += `
  • ${lastDateofLastMonth - i + 1}
  • `; }; for(let i = 1; i<= lastDateofMonth; i++) { let isToday = i === new Date().getDate() && currMonth === new Date().getMonth() && currYear === new Date().getFullYear() ? "active" : ""; liTag += `
  • ${i}
  • `; }; for(let i = lastDayofMonth; i<= 5; i++) { liTag += `
  • ${i - lastDayofMonth + 1}
  • `; }; // 不再直接操作DOM,而是更新状态,让React来处理渲染 setCalendarDaysHtml(liTag); }, [currYear, currMonth]); // 依赖项:当currYear或currMonth变化时,renderCalendar函数才会重新创建 // ... 后续代码 };

    现在,renderCalendar函数不再直接操作DOM,而是通过setCalendarDaysHtml更新状态。当calendarDaysHtml状态更新时,React会根据新的HTML字符串重新渲染对应的DOM部分。

    4. 使用useEffect管理副作用与初始化

    useEffect钩子用于处理组件的副作用,例如数据获取、订阅、手动更改DOM以及在React组件中集成其他非React库。对于日历的首次渲染以及月份或年份变化时的更新,renderCalendar函数应该在useEffect中调用。

    // ... (之前的代码)
    
    export const Calendar = () => {
      // ... (useState和renderCalendar定义)
    
      // 使用useEffect在组件挂载和currYear/currMonth变化时调用renderCalendar
      useEffect(() => {
        // 首次渲染和月份/年份变化时,执行日历渲染逻辑
        renderCalendar();
      }, [renderCalendar]); // 依赖项:当renderCalendar函数(因其内部依赖变化而重新创建时)变化时,此effect会重新运行
    
      // 处理箭头点击事件
      const handleArrowClick = useCallback((id) => {
        let newMonth = currMonth;
        let newYear = currYear;
    
        if (id === "prev") {
          newMonth--;
        } else {
          newMonth++;
        }
    
        // 处理月份越界,更新年份
        if(newMonth < 0 || newMonth > 11) {
          const newDate = new Date(newYear, newMonth);
          newYear = newDate.getFullYear();
          newMonth

    热门AI工具

    更多
    DeepSeek
    DeepSeek

    幻方量化公司旗下的开源大模型平台

    豆包大模型
    豆包大模型

    字节跳动自主研发的一系列大型语言模型

    通义千问
    通义千问

    阿里巴巴推出的全能AI助手

    腾讯元宝
    腾讯元宝

    腾讯混元平台推出的AI助手

    文心一言
    文心一言

    文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

    讯飞写作
    讯飞写作

    基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

    即梦AI
    即梦AI

    一站式AI创作平台,免费AI图片和视频生成。

    ChatGPT
    ChatGPT

    最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

    相关专题

    更多
    js 字符串转数组
    js 字符串转数组

    js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

    298

    2023.08.03

    js截取字符串的方法
    js截取字符串的方法

    js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

    212

    2023.09.04

    java基础知识汇总
    java基础知识汇总

    java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

    1500

    2023.10.24

    字符串介绍
    字符串介绍

    字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

    623

    2023.11.24

    java读取文件转成字符串的方法
    java读取文件转成字符串的方法

    Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

    613

    2024.03.22

    php中定义字符串的方式
    php中定义字符串的方式

    php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

    588

    2024.04.29

    go语言字符串相关教程
    go语言字符串相关教程

    本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

    170

    2025.07.29

    c++字符串相关教程
    c++字符串相关教程

    本专题整合了c++字符串相关教程,阅读专题下面的文章了解更多详细内容。

    83

    2025.08.07

    php中文乱码如何解决
    php中文乱码如何解决

    本文整理了php中文乱码如何解决及解决方法,阅读节专题下面的文章了解更多详细内容。

    0

    2026.01.28

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    Django 教程
    Django 教程

    共28课时 | 3.6万人学习

    React 教程
    React 教程

    共58课时 | 4.2万人学习

    Bootstrap4.x---十天精品课堂
    Bootstrap4.x---十天精品课堂

    共22课时 | 1.7万人学习

    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号 技术交流群
    PHP中文网订阅号
    每天精选资源文章推送

    Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号