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

React Native中Jest+RN测试库模拟点击后useRef值变更失败求助

问题描述

我有一个名为MyComponent的组件,挂载时会调用两个API:初始显示第一个API的数据,点击指定按钮后切换显示第二个API的数据,使用useRef控制数据切换逻辑。组件代码如下:

const MyComponent = () => {
  const random = React.useRef(false);
  
  const { data, isFetching, isError } = useMakeFirstAPICall();
  const { data: a, isFetching: b, isError: c, refetch: d } = useMakeSecondAPICall();

  const usedData = random.current ? a : data;
  const usedIsFetching = random.current ? b : isFetching;
  const usedIsError = random.current ? c : isError;

  return (
    <View>
      <Pressable
        testID="my.btn"
        onPress={() => {
          random.current = true;
          d();
        }}
      >
        <MyCustomIcon />
      </Pressable>
      {!usedIsFetching && <ShowData data={usedData} />}
    </View>
  );
}

测试时我已Mock API调用,但点击按钮后数据未更新,尝试Mock useRef也无效。我的测试代码如下:

jest.mock("src/api1", () => ({
  useMakeFirstAPICall: () => ({
    data: MockData1,
  }),
}));

jest.mock("src/api2", () => ({
  useMakeSecondAPICall: () => ({
    data: MockData2,
  }),
}));

describe("Testing MyComponent", () => {
  test("Test button press action", async () => {

    const reference = { current: true };
    Object.defineProperty(reference, "current", {
      get: jest.fn(() => null),
      set: jest.fn(() => null),
    });
    jest.spyOn(React, "useRef").mockReturnValueOnce(reference);

    render(<MyComponent />);

    const gridList1 = await screen.findAllByTestId("grid.card");
    expect(gridList1).toBeTruthy();
    expect(gridList1.length).toBe(5);

    const myButton = await screen.findByTestId("my.btn");
    await fireEvent(myButton, "click");

    const gridList2 = await waitFor(() => screen.findAllByTestId("grid.card"));
    expect(gridList2).toBeTruthy();
    expect(gridList2.length).toBe(2);
  });
});

测试时报错(错误截图显示找不到grid.card元素),请问我哪里出错了?


问题原因与修复方案

1. 组件核心逻辑错误:useRef无法触发重渲染

useRef的更新不会触发React组件重渲染。你点击按钮时修改了random.current = true,但组件不会重新计算usedData、usedIsFetching这些变量,UI自然不会切换到第二个API的数据。

修复组件逻辑:
把useRef换成useState,因为useState的状态更新会触发组件重渲染:

const MyComponent = () => {
  // 用useState替代useRef,管理是否切换到第二个API
  const [useSecondAPI, setUseSecondAPI] = React.useState(false);
  
  const { data, isFetching, isError } = useMakeFirstAPICall();
  const { data: secondData, isFetching: secondIsFetching, isError: secondIsError, refetch: refetchSecond } = useMakeSecondAPICall();

  const usedData = useSecondAPI ? secondData : data;
  const usedIsFetching = useSecondAPI ? secondIsFetching : isFetching;
  const usedIsError = useSecondAPI ? secondIsError : isError;

  return (
    <View>
      <Pressable
        testID="my.btn"
        onPress={() => {
          setUseSecondAPI(true);
          refetchSecond();
        }}
      >
        <MyCustomIcon />
      </Pressable>
      {!usedIsFetching && <ShowData data={usedData} />}
    </View>
  );
}

2. 测试代码中Mock useRef的致命错误

你Mock的useRef返回的current getter始终返回null,导致组件初始时usedData为null,根本不会渲染ShowData组件,所以初始断言就会找不到grid.card元素直接报错。而且就算组件逻辑正确,Mock useRef完全没必要。

修复测试代码:
移除对useRef的Mock,同时补充API Mock的完整字段(包括isFetching,避免逻辑异常):

jest.mock("src/api1", () => ({
  useMakeFirstAPICall: () => ({
    data: MockData1,
    isFetching: false,
    isError: false
  }),
}));

jest.mock("src/api2", () => ({
  useMakeSecondAPICall: () => ({
    data: MockData2,
    isFetching: false,
    isError: false,
    refetch: jest.fn() // Mock refetch函数
  }),
}));

describe("Testing MyComponent", () => {
  test("Test button press action", async () => {
    render(<MyComponent />);

    // 验证初始显示第一个API的数据
    const gridList1 = await screen.findAllByTestId("grid.card");
    expect(gridList1.length).toBe(5);

    // 点击切换按钮
    const myButton = screen.getByTestId("my.btn");
    fireEvent.click(myButton);

    // 等待UI更新,验证第二个API的数据
    await waitFor(() => {
      const gridList2 = screen.findAllByTestId("grid.card");
      expect(gridList2.length).toBe(2);
    });
  });
});

3. 额外注意事项

  • 确保MockData1包含5条数据、MockData2包含2条数据,和测试断言匹配。
  • 测试中无需用await获取按钮,组件挂载后按钮已存在,直接用screen.getByTestId即可。
  • 必须Mockrefetch为jest函数,避免调用真实API逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 09:50:50