自定义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
相关产品推荐
相关产品推荐

