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

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钩子来缓存函数实例。

// ... (之前的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  11) {      const newDate = new Date(newYear, newMonth);      newYear = newDate.getFullYear();      newMonth

    以上就是React 函数组件中DOM操作与副作用管理:构建可靠日历组件的实践的详细内容,更多请关注创想鸟其它相关文章!

    版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
    发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/143236.html

    (0)
    打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
    上一篇 2025年12月1日 18:03:06
    下一篇 2025年12月1日 18:03:16

    相关推荐

    发表回复

    登录后才能评论
    关注微信