You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Vitest测试useLocalTime Hook时renderHook返回null的问题

测试useLocalTime Hook时renderHook返回null的原因及解决办法

我用Vitest测试自定义React Hook useLocalTime,但@testing-library/react提供的renderHook函数始终返回null,希望排查原因。

Hook代码

import {useEffect, useState} from "react";
import {Dayjs} from "dayjs";

interface Props {
    dayJsObject: Dayjs;
    format?: string;
}

const capitalizeFirstLetters = (text: string) => {
    return text
        .toLowerCase()
        .split(" ")
        .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
        .join(" ");
};

export const useLocalTime = ({dayJsObject, format}: Props) => {
    const [formattedTime, setFormattedTime] = useState<string | null>(null);

    useEffect(() => {
        const updateTime = () => {
            const currentTime = dayJsObject.format(format ?? "D MMMM YYYY LTS");

            setFormattedTime(capitalizeFirstLetters(currentTime));
        };

        const interval = setInterval(updateTime, 1000);

        return () => clearInterval(interval);
    }, [dayJsObject, format]);

    return formattedTime;
};

测试代码

import {renderHook} from "@testing-library/react";
import dayjs from "dayjs";
import {useLocalTime} from "ui/hooks/useLocalTime";

describe("useLocalTime", () => {
    it("should return the formatted time", () => {
        const dayJsObject = dayjs("2022-06-16T14:49:35.718Z");
        const {result} = renderHook(() => useLocalTime({dayJsObject}));

        expect(result.current).toEqual("16 June 2022 14:49:35");
    });
});

报错信息

AssertionError: expected null to deeply equal '16 June 2022 14:49:35'

- Expected: 
"16 June 2022 14:49:35"

+ Received: 
null

原因分析

  1. 初始状态为null:Hook的formattedTime初始值设为null,而useEffect内部仅通过setInterval设置了1秒后执行的更新逻辑,没有立即触发时间格式化操作。
  2. 测试时机过早:测试代码在renderHook后立刻断言,此时useEffect还没完成首次执行,定时器也未触发,result.current仍保持初始的null。

解决办法

方法一:修改Hook,初始化时立即执行格式化

在useEffect中先手动调用一次updateTime,确保Hook首次渲染就返回格式化后的时间:

useEffect(() => {
    const updateTime = () => {
        const currentTime = dayJsObject.format(format ?? "D MMMM YYYY LTS");
        setFormattedTime(capitalizeFirstLetters(currentTime));
    };

    updateTime(); // 立即执行一次,初始化状态
    const interval = setInterval(updateTime, 1000);

    return () => clearInterval(interval);
}, [dayJsObject, format]);

方法二:修改测试代码,等待状态更新

使用@testing-library/react的waitFor工具,等待状态完成更新后再断言:

import {renderHook, waitFor} from "@testing-library/react";
import dayjs from "dayjs";
import {useLocalTime} from "ui/hooks/useLocalTime";

describe("useLocalTime", () => {
    it("should return the formatted time", async () => {
        const dayJsObject = dayjs("2022-06-16T14:49:35.718Z");
        const {result} = renderHook(() => useLocalTime({dayJsObject}));

        await waitFor(() => {
            expect(result.current).toEqual("16 June 2022 14:49:35");
        });
    });
});

方法三:使用虚拟定时器快进时间(Vitest)

借助Vitest的虚拟定时器功能,直接快进1秒触发定时器逻辑:

import {renderHook} from "@testing-library/react";
import dayjs from "dayjs";
import {useLocalTime} from "ui/hooks/useLocalTime";

describe("useLocalTime", () => {
    it("should return the formatted time", () => {
        vi.useFakeTimers(); // 启用虚拟定时器
        const dayJsObject = dayjs("2022-06-16T14:49:35.718Z");
        const {result} = renderHook(() => useLocalTime({dayJsObject}));

        vi.advanceTimersByTime(1000); // 快进1秒
        expect(result.current).toEqual("16 June 2022 14:49:35");
        
        vi.useRealTimers(); // 恢复真实定时器
    });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 12:23:38