useHistory hook inside useEffect create infinite loop in jest tests

Viewed 1088

I'm very annoyed since the morning cause I can't test my custom hooks without this infinite loop.

My custom hook look like :

useGetObj1.tsx

export function useGetObj1(): [
  { obj1: IObj1 | null; obj2: IObj2 | null } | null,
  Function
] {
  const User = useSelector((state: IStoreState) => state.User);
  const history: History = useHistory();
  const resState = useState<{ obj1: IObj1 | null; obj2: IObj2 | null } | null>(null);
  const setState = resState[1];
  useEffect(() => {
    async function setStateValue() {
      let tmp = null;
      const res = await ParseVerifObj1(User, history.location.pathname);
      if (typeof res !== "number") {
        res
          ? (tmp = {
              obj1: res.obj1,
              obj2: res.obj2,
            })
          : (tmp = { obj1: null, obj2: null });
        setState(tmp);
      } else if (res === -1) history.push("/HomePage");
    }
    setStateValue();
  }, [history.location.pathname, User, setState]);
  return resState;
}

My test looks like :

useGetObj1.spec.js

jest.mock("axios");
jest.mock("react-router-dom", () => ({
  useHistory: () => ({
    push: jest.fn(),
    location: {
      pathname: "/path/web",
    },
  }),
}));

// make setup-jest file and put it later
import "axios";
import "babel-polyfill";
//
import React from "react";
import { mount } from "enzyme";
import { Provider } from "react-redux";
// import ParseVerifObj1 to mock it
import * as ParseVerifObj1 from "../path";
// custom hook tested here
import { useGetObj1 } from "../path";
import configureStore from "redux-mock-store";
// act to handle the Promise and the re-render of the renderHook
import { act } from "react-dom/test-utils";
// Utils Initial store
import { initialStore } from "../path";
import routeData from "react-router";

const mockStore = configureStore();

const mockLocation = {
  pathname: "/",
  hash: "",
  search: "",
  state: "",
};
jest.spyOn(routeData, "useLocation").mockReturnValue(mockLocation);

describe("useGetObj1.spec.js", () => {
  let store;
  beforeEach(() => {
    store = mockStore(initialStore);
    jest.clearAllMocks();
  });

  let results;
  let wrapper;
  const renderHook = (hook) => {
    function HookWrapper() {
      results = hook();
      return null;
    }
    wrapper = mount(
      <Provider store={store}>
        <HookWrapper />
      </Provider>
    );
    return results;
  };

  describe("Testing the custom hook : useGetObj1", () => {
    it("useGetLesson is ran", async () => {
      await act(async () => renderHook(useGetObj1));
      expect(wrapper).toBeTruthy();
    });

    it("Testing the output adress values get from the ParseVerifObj1 function", async () => {
      const spy = jest.spyOn(ParseVerifObj1, "ParseVerifObj1");
      const lesson = Symbol("Obj1");
      const session = Symbol("Obj2");
      spy.mockReturnValue({
        Obj1,
        Obj2,
      });
      await act(async () => renderHook(useGetObj1));
      expect(spy).toHaveBeenCalledWith(
        initialStore.User,
        "/path/web"
      );
      expect(spy).toHaveBeenCalled();
      expect(results[0].Obj1).toBe(Obj1);
      expect(results[0].Obj2).toBe(Obj2);
      expect(results[1]).toBeInstanceOf(Function);
    });

I know it's not a good thing to put history in the useEffect's dependencies but I got a warning like

Line 39:6: React Hook useEffect has a missing dependency: 'history'. Either includes it or remove the dependency array and I tried a lot of different implementation but no one fixed the problem and compile without a warning

thx for helping :)


_Solution


The solution for me was to use useRef to avoid the dependencies requirement like this :

const history: History = useRef(useHistory());

with the ref you do not need anymore to put it as a dependency

0 Answers
Related