skip to Main Content

I have a code snippet here from NextJS 13

export default function Members() {
  // States
  const [users, setUsers] = useState<MemberType[]>([]);
  const [loading, setLoading] = useState(false);
  const searchParams = useSearchParams();
  // To refresh the page
  const router = useRouter();
  const inputRef = useRef<HTMLInputElement | null>(null);

  // Function to fetch a book
  const fetchBooks = async (userQuery: string | null) => {
    if (!userQuery) {
      const res = await fetch("/api/users/members", {
        method: "GET",
        headers: { "Content-Type": "application/json" },
      });
      const body: MemberType[] = await res.json();
      return body;
    }
    const res = await fetch(`/api/users/members/${userQuery}`, {
      method: "GET",
      headers: { "Content-Type": "application/json" },
    });
    const body: MemberType[] = await res.json();
    return body;
  };

  // Runs if the search parameters change
  useEffect(() => {
    setLoading(true);
    const searched = searchParams.get("search");
    console.log(searched);
    
    fetchBooks(searched).then((users) => {
      setUsers(users);
      setLoading(false);
    });
    if (searched && inputRef.current) {
      inputRef.current.value = searched;
    }
  }, [searchParams]);
  //...

And I am testing the Members function using Jest.

In the test file I have this mock

jest.mock("next/navigation", () => ({
  useRouter: () => ({
    push: jest.fn(),
  }),
  useSearchParams: () => ({
    get: jest.fn().mockReturnValue(''),
  }),
}));

and this simple test

describe("Members page", () => {
  it("should render the page correctly", async () => {
    await act(async () => {
      render(<Members />);
      
    });
  });
});

However, the test times out and it seems like that the useEffect hook is running infinitely. I suspect that it is something to do with the get function in the mock which triggers the useEffect hook again. I have been looking if there is anyway to change this mock?

2

Answers


  1. You are returning a new object every time useSearchParams is called

    Do this instead:

    jest.mock("next/navigation", () => {
      const searchParams = {
        get: jest.fn().mockReturnValue(""),
      };
      return {
        useRouter: () => ({
          push: jest.fn(),
        }),
        useSearchParams: () => searchParams,
      };
    });
    
    Login or Signup to reply.
  2. I believe it would be safer to just listen to the field changes that you really need, in your case search, especially when it’s a primitive string.

    const searched = searchParams.get("search"); // move outside useEffect
    
    useEffect(() => {
        setLoading(true);
        console.log(searched);
        
        fetchBooks(searched).then((users) => {
          setUsers(users);
          setLoading(false);
        });
    
        if (searched && inputRef.current) {
          inputRef.current.value = searched;
        }
    }, [searched]); // call only when "searched" changes
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search