
在react中,组件的渲染是声明式的:你描述ui在给定状态下应该是什么样子,react会负责高效地更新dom以匹配这个描述。直接使用document.queryselector、innertext或innerhtml来操作dom元素,尤其是在函数组件的顶层(即组件函数体内部,return语句之前),是与react的声明式范式相悖的。
原始代码中存在以下问题:
在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会自动触发组件的重新渲染,从而反映最新的日期。
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 += `<li class= "inactive">${lastDateofLastMonth - i + 1}</li>`;
};
for(let i = 1; i<= lastDateofMonth; i++) {
let isToday = i === new Date().getDate() && currMonth === new Date().getMonth()
&& currYear === new Date().getFullYear() ? "active" : "";
liTag += `<li class="${isToday}">${i}</li>`;
};
for(let i = lastDayofMonth; i<= 5; i++) {
liTag += `<li class= "inactive">${i - lastDayofMonth + 1}</li>`;
};
// 不再直接操作DOM,而是更新状态,让React来处理渲染
setCalendarDaysHtml(liTag);
}, [currYear, currMonth]); // 依赖项:当currYear或currMonth变化时,renderCalendar函数才会重新创建
// ... 后续代码
};现在,renderCalendar函数不再直接操作DOM,而是通过setCalendarDaysHtml更新状态。当calendarDaysHtml状态更新时,React会根据新的HTML字符串重新渲染对应的DOM部分。
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以上就是React 函数组件中DOM操作与副作用管理:构建可靠日历组件的实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号