You need to enable JavaScript to run this app. React开发倒计时组件提前调用未渲染DOM元素报错如何解决
优惠活动
大模型
产品
解决方案
定价
更多

React开发倒计时组件提前调用未渲染DOM元素报错如何解决

问题原因

你在函数组件的同步执行阶段直接调用countdown(),此时return语句还未执行,对应的DOM元素还未渲染到页面中,document.querySelector无法匹配到对应元素会返回null,对null赋值innerText自然会抛出类型错误。就算你把countdown调用移到return语句之后也不会生效,因为return是函数执行的终止语句,后续代码不会运行。另外注意React自定义组件名必须采用大驼峰命名,否则会被识别为原生HTML标签导致渲染异常。

解决方案

方案1:保留原生DOM操作逻辑,使用useEffect确保DOM挂载后执行

借助React的useEffect钩子,其回调会在组件挂载完成后执行,此时DOM已经渲染完成,可以正常查询到对应元素:

import { useEffect } from 'react';

function Maintenance() {
    const countdown = () => {
        const countDate = new Date("August 10, 2021 00:00:00").getTime();
        const now = new Date().getTime();
        const gap = countDate - now;

        // 时间单位定义
        const second = 1000;
        const minute = second * 60;
        const hour = minute * 60;
        const day = hour * 24;

        // 计算剩余时间,小于0时默认显示0
        const textDay = Math.max(0, Math.floor(gap / day));
        const textHour = Math.max(0, Math.floor((gap % day) / hour ));
        const textMinute = Math.max(0, Math.floor((gap % hour) / minute));
        const textSecond = Math.max(0, Math.floor((gap % minute) / second ));

        document.querySelector('.day').innerText = textDay;
        document.querySelector('.hour').innerText = textHour;
        document.querySelector('.minute').innerText = textMinute;
        document.querySelector(".second").innerText = textSecond;
    };

    // 组件挂载完成后执行,且每秒更新一次倒计时
    useEffect(() => {
        countdown();
        const timer = setInterval(countdown, 1000);
        // 组件卸载时清理定时器,避免内存泄漏
        return () => clearInterval(timer);
    }, []);

    return (
        <section className="coming-soon">
            <div>
            <h1>A nossa equipa está a trabalhar para voltar a por o site no ar</h1>
            <div className="countdown">
                <div className="container-day">
                    <h3 className="day">Time</h3>
                    <h3>Dias</h3>
                </div>

                <div className="container-hour">
                    <h3 className="hour">Time</h3>
                    <h3>Horas</h3>
                </div>

                <div className="container-minute">
                    <h3 className="minute">Time</h3>
                    <h3>Minutos</h3>
                </div>

                <div className="container-seconds">
                    <h3 className="second">Time</h3>
                    <h3>Segundos</h3>
                </div>

            </div>
            
            </div>
            <img className="waiting" src="https://res.cloudinary.com/dnho57ne8/image/upload/v1628004034/3685835_sp5t6h.svg" alt="等待插图"/>
        </section>
    )
}

export default Maintenance;

方案2(推荐):使用React状态驱动,避免原生DOM操作

这是更符合React开发规范的写法,不需要手动操作DOM,直接把剩余时间存在state里,渲染到JSX中即可:

import { useState, useEffect } from 'react';

function Maintenance() {
    // 定义状态存储剩余时间
    const [timeLeft, setTimeLeft] = useState({
        day: 0,
        hour: 0,
        minute: 0,
        second: 0
    });

    const calculateTimeLeft = () => {
        const countDate = new Date("August 10, 2021 00:00:00").getTime();
        const now = new Date().getTime();
        const gap = countDate - now;

        const second = 1000;
        const minute = second * 60;
        const hour = minute * 60;
        const day = hour * 24;

        setTimeLeft({
            day: Math.max(0, Math.floor(gap / day)),
            hour: Math.max(0, Math.floor((gap % day) / hour )),
            minute: Math.max(0, Math.floor((gap % hour) / minute)),
            second: Math.max(0, Math.floor((gap % minute) / second ))
        });
    };

    useEffect(() => {
        calculateTimeLeft();
        const timer = setInterval(calculateTimeLeft, 1000);
        return () => clearInterval(timer);
    }, []);

    return (
        <section className="coming-soon">
            <div>
            <h1>A nossa equipa está a trabalhar para voltar a por o site no ar</h1>
            <div className="countdown">
                <div className="container-day">
                    <h3 className="day">{timeLeft.day}</h3>
                    <h3>Dias</h3>
                </div>

                <div className="container-hour">
                    <h3 className="hour">{timeLeft.hour}</h3>
                    <h3>Horas</h3>
                </div>

                <div className="container-minute">
                    <h3 className="minute">{timeLeft.minute}</h3>
                    <h3>Minutos</h3>
                </div>

                <div className="container-seconds">
                    <h3 className="second">{timeLeft.second}</h3>
                    <h3>Segundos</h3>
                </div>

            </div>
            
            </div>
            <img className="waiting" src="https://res.cloudinary.com/dnho57ne8/image/upload/v1628004034/3685835_sp5t6h.svg" alt="等待插图"/>
        </section>
    )
}

export default Maintenance;

注:示例中使用的目标时间为2021年8月10日,当前时间已超过该节点,倒计时会默认显示为0,可根据需求自行修改目标时间。

内容的提问来源于stack exchange,提问作者Tomas Mota

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.09 14:18:02