epignosisx
epignosisx

Reputation: 6192

How to hook with useEffect/setState

I'm having trouble making the following test pass:

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({});
  });
});

It keeps failing:

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 | });

I have created a repro case in codesandbox:

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

Upvotes: 1

Views: 899

Answers (1)

backtick
backtick

Reputation: 2775

renderHook doesn't wait for your setTimeout to fire; it can't know what 'side effects' your component has. So when your expect() runs, the current value is still its default - null.

We can force the test to wait until the hook updates again by using waitForNextUpdate, which is on the object renderHook returns. waitForNextUpdate is a function that returns a promise that resolves once the hook is updated again (e.g. when your setTimeout fires).

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({});
  });
});

Upvotes: 2

Related Questions