如何在Jest+React Testing Library中访问与修改React Context?
我帮你梳理下核心问题:你的Context配置和测试代码里都存在关键错误,导致组件无法感知loading状态的变化。下面一步步拆解修复:
1. 先修正生产环境的Provider配置错误
你在index.js里给ThemesProvider手动传了空的value={{}},这会直接覆盖Provider内部生成的所有上下文值(包括loading、fetchThemes等方法),这是最致命的问题!
错误代码:
import { ThemesProvider } from "./context/ThemeProvider"; ReactDOM.render( <ThemesProvider value={{}}> <App /> </ThemesProvider>, document.getElementById("root") );
修复后:
删掉手动传入的value,因为ThemesProvider内部已经帮你维护好了完整的上下文状态:
import { ThemesProvider } from "./context/ThemeProvider"; ReactDOM.render( <ThemesProvider> <App /> </ThemesProvider>, document.getElementById("root") );
2. 测试代码的3个关键错误修复
错误1:直接从Context对象解构loading
你写的const { loading } = AvailableThemesContext;完全无效——AvailableThemesContext是用React.createContext()创建的上下文容器,本身不存储状态,必须通过useContext(AvailableThemesContext)或者Consumer才能获取到Provider内部的状态。直接删掉这行代码即可。
错误2:使用了错误的Provider名称
测试里写的<ThemeProvider value={{ loading: false}}>少了个s,你的Provider导出的是ThemesProvider,名称不匹配会导致组件根本没被正确的上下文包裹。
错误3:未正确用Provider包裹测试组件
修复后的测试代码应该用正确的ThemesProvider包裹组件,并传入需要模拟的状态:
import React from "react"; import { render, screen } from "./test-utils"; import CalendarView from "../views/CalendarView"; import { ThemesProvider } from "../context/ThemeProvider"; // 导入正确的Provider describe("<CalendarView />", () => { test("渲染加载器当loading为true时", async () => { render( <ThemesProvider value={{ loading: true, contextTheme: {} }}> <CalendarView /> </ThemesProvider> ); expect(screen.getByLabelText("audio-loading")).toBeInTheDocument(); }); test("渲染选择器组件当loading为false时", () => { render( <ThemesProvider value={{ loading: false, contextTheme: {} }}> <CalendarView /> </ThemesProvider> ); expect(screen.getByTestId("picker-component")).toBeInTheDocument(); }); });
如果你的test-utils里有自定义的render方法支持上下文覆盖,也可以简化成:
test("渲染选择器组件当loading为false时", () => { render(<CalendarView />, { context: { AvailableThemesContext: { loading: false, contextTheme: {} } } }); expect(screen.getByTestId("picker-component")).toBeInTheDocument(); });
3. 额外优化:给Context设置默认值
为了避免组件在未被Provider包裹时崩溃,建议创建Context时设置默认值:
const AvailableThemesContext = React.createContext({ contextTheme: {}, loading: true, fetchThemes: () => {}, refresh: () => {}, setLoading: () => {}, });
最后再确认下你的CalendarView组件是否正确通过useContext获取状态:
import { useContext } from "react"; import AvailableThemesContext from "../context/ThemeProvider"; const CalendarView = () => { const { loading } = useContext(AvailableThemesContext); if (loading) { return <div aria-label="audio-loading">加载中...</div>; } return <div data-testid="picker-component">选择器组件</div>; };
内容的提问来源于stack exchange,提问作者CodeAlga

