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

自定义Fetch Hook缓存机制测试失败,如何排查解决?

问题分析与解决方案

核心问题

你的测试用例失败的主要原因是未等待首次请求完成就执行rerender,同时代码中存在几处细节错误导致缓存逻辑不符合预期。


具体问题点与修复方案

1. 测试未等待首次请求完成,缓存未被填充

测试中第一次渲染后立即调用rerender,此时异步请求还未完成,cache[url]尚未被赋值,导致第二次useEffect执行时依然会发起新的请求,最终fetch被调用两次。

修复测试代码:

import { render, waitFor } from "@testing-library/react";
import { RocketsList } from "./List"; // 确保导入正确的组件

// 提前mock fetch,避免实际网络请求
beforeEach(() => {
  jest.spyOn(window, 'fetch').mockResolvedValue({
    json: jest.fn().mockResolvedValue([])
  });
});

afterEach(() => {
  jest.restoreAllMocks();
  // 清空模块级缓存,避免影响其他测试用例
  const { cache } = require('../hooks/useFetch');
  Object.keys(cache).forEach(key => delete cache[key]);
});

const renderList = (filterParams = testFilterParamsList[0]) =>
    render(<RocketsList filterParams={filterParams} />);

it("should re-render without fetch", async () => {
    const { rerender } = renderList(testFilterParamsList[0]);
    // 等待首次请求完成,确保缓存已被写入
    await waitFor(() => expect(window.fetch).toHaveBeenCalledTimes(1));
    // 执行rerender
    rerender(<RocketsList filterParams={testFilterParamsList[1]} />);
    // 验证fetch未被再次调用
    expect(window.fetch).toHaveBeenCalledTimes(1);
});

2. useFetch参数不匹配(代码笔误)

RocketsList组件调用useFetch时传入的是name属性,但useFetch钩子接收的是filterOptions参数,导致filterOptions始终为undefined。这会让useEffect的依赖项无法感知到筛选参数的变化(和你描述的浏览器中缓存生效的现象矛盾,推测是代码笔误)。

修复List.js:

import useFetch from "../hooks/useFetch";

export const RocketsList = ({ filterParams }) => {
  const { isPending, error, data } = useFetch({
    url: "https://api.spacexdata.com/v3/launches/past",
    filterOptions: filterParams, // 替换原有的name,匹配useFetch的参数定义
  });

  return (
    <div>
      Doesn't matter 
    </div>
  );
};

3. FETCH_ERROR常量定义错误

useFetch中FETCH_ERROR被错误定义为"FETCH_SUCCESS",会导致错误状态无法正确触发。

修复useFetch.js:

// 修正常量定义
const FETCH_ERROR = "FETCH_ERROR";

额外优化:缓存键结合筛选条件(可选)

当前缓存仅基于url,如果不同筛选条件需要独立缓存,建议将filterOptions序列化后和url组合作为缓存键:

// 在useFetch的useEffect中修改缓存逻辑
const fetchData = async () => {
    dispatch({type: FETCH_REQUEST});
    // 生成包含筛选条件的缓存键
    const cacheKey = `${url}-${JSON.stringify(filterOptions)}`;
    if (cache[cacheKey]) {
        const data = cache[cacheKey];
        dispatch({type: FETCH_SUCCESS, payload: data});
    } else {
        try {
            const response = await window.fetch(url);
            let data = await response.json();
            cache[cacheKey] = data; // 缓存到组合键中
            dispatch({type: FETCH_SUCCESS, payload: data});
        } catch (err) {
            dispatch({type: FETCH_ERROR, payload: err});
        }
    }
};

内容的提问来源于stack exchange,提问作者t.cankurt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 08:35:19