首页 > 解决方案 > 如何与 useEffect/setState 挂钩

问题描述

我无法通过以下测试:

import { useEffect, useState } from "react";

export function useComponentResources(required) {
  const [componentResources, setComponentResources] = useState(null);

  useEffect(() => {
    if (required) {
      // api call
      setTimeout(() => setComponentResources({}), 100);
    }
  }, [required]);

  return componentResources;
}
import { renderHook } from "@testing-library/react-hooks";
import { useComponentResources } from "./component-resources.hook";

describe("component-resources.hook", () => {
  it("fetches resources when required", () => {
    //act
    const { result } = renderHook(() => useComponentResources(true));

    //assert
    expect(result.current).toEqual({});
  });
});

它一直失败:

expect(received).toEqual(expected)

Expected value to equal:
  {}
Received:
  null

Difference:

  Comparing two different types of values. Expected object but received null.

   7 |     const { result } = renderHook(() => useComponentResources(true));
   9 |     //assert
> 10 |     expect(result.current).toEqual({});
  11 |   });
  12 | });

我在代码沙箱中创建了一个复制案例:

https://codesandbox.io/embed/priceless-browser-94ec2

标签: react-hooks-testing-library

解决方案


renderHook不等你setTimeout开火;它不知道你的组件有什么“副作用”。因此,当您expect()运行时,当前值仍然是其默认值 - null

我们可以强制测试等到钩子再次更新,方法waitForNextUpdate是使用对象renderHook返回。waitForNextUpdate是一个返回承诺的函数,一旦钩子再次更新(例如当你的 setTimeout 触发时),该承诺就会解决。

import { renderHook } from "@testing-library/react-hooks";
import { useComponentResources } from "./component-resources.hook";

describe("component-resources.hook", () => {
  it("fetches resources when required", async () => {
    const { result, waitForNextUpdate } = renderHook(() => useComponentResources(true));

    await waitForNextUpdate();

    expect(result.current).toEqual({});
  });
});

推荐阅读