reactjsreact-hooksreact-reduxreact-test-renderer

Infinite loop in useEffect after an API mock in Jest


Seeing some weird things within the component that I am currently working on. So, I am having a server side paginated table, On every API call I am pushing the new records to the existing array. I am using fetchBaseQuery to make an API call.

Code to achieve it

let response = useGetQuery(request); // This hook will perform the API call
const records =
    response.data?.records;
  React.useEffect(() => {
    if (records) {
      setTableRecords((prevState: any) => [
        ...prevState,
        ...records,
      ]);
    }
  }, [records]);

Test case for this


jest.mock("../endpoints", () => ({
  ...jest.requireActual("../endpoints"),
  useGetQuery: () => ({
    isSuccess: true,
    data: {
      message: "Success",
      records: [], // It gets into infinte loop if I provide this key while mocking
    },
  }),
}));

test("should mount", () => {
    const component = renderer.create(<SomeComponent/>);
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
});

As, per my understanding if we use array as a dependency in the useEffect hook, it might lead to an infinite loop as React uses shallow comparison to check if the dependency’s reference has changed. To fix this, I tried using useRef hook, but that doesn't work as per my use case as useRef doesn’t notify you when its content changes.

I then tried by making the response.data object as a dependency to the useEffect hook, but as per the shallow comparison it is also leading to the infinite loop, so I was then trying to fix it with useMemo hook but got no luck as if I pass the dependency as the object it was again leading to the same problem & passing no dependency doesn't work for me.

Is there any other way that I can handle this situation better?


Solution

  • Stringify the Array that you are passing in the dependency array if the array is not too large.

    let response = useGetQuery(request); // This hook will perform the API call
    const records =
        response.data?.records;
      React.useEffect(() => {
        if (records) {
          setTableRecords((prevState: any) => [
            ...prevState,
            ...records,
          ]);
        }
      }, [JSON.stringify(records)]);