I have the following hook :
const useBar = () => {
const [myFoo, setFoo] = useState(0);
const [myBar, setBar] = useState(0);
useEffect(() => {
setFoo(myFoo + 1);
console.log("setting foo (1)", myFoo, myBar);
}, [setFoo, myFoo, myBar]);
useEffect(() => {
setBar(myBar + 1);
console.log("setting bar (2)", myFoo, myBar);
}, [setBar, myBar, myFoo]);
};
When working with a component I have the expected behaviour of infinite loop :
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const Bar = () => {
useBar();
return <div>Bar</div>;
};
function App() {
return (
<Bar />
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
From console.log :
setting foo (1) 1 1
setting bar (2) 1 1
setting foo (1) 2 2
setting bar (2) 2 2
setting foo (1) 3 3
setting bar (2) 3 3
...
However testing this with @testing-library/react-hooks
gives only the first loop output :
describe("Tests", () => {
test("useBar", async () => {
const { rerender } = renderHook(() => {
return useBar();
});
// await waitForNextUpdate();
// tried the above doesn't work
// rerender();
// with rerender it loops through the next "cycle"
});
});
Was wondering how to test properly hooks, without calling rerender
and mimic the same behaviour as React - to re-render the component on state changes.
I guess you want to test a hook
that uses useEffect
and report an error when an infinite loop occurs.
But in fact we can't test an infinite loop, we can only set a limit on the number of times. When the loop exceeds this limit, we think that it will continue to loop indefinitely. As for whether it is actually infinite loop, it is not easy prove.
However, it is not easy to test a repetition of useEffect
.
Although using waitForNextUpdate
can get a timeout error, this error can also be caused by a long time asynchronous operation, and as a unit test, you need fast feedback. You can't wait for the timeout every time. It's a waste of time.
Thanks for this issues, I found the direction.Provide a way to trigger useEffect from tests
When I replace useEffect
with useLayoutEffect
, I can get a Maximum update depth exceeded
error quickly and accurately. This only needs to replace useEffect
with useLayoutEffect
in the required test. The test will fail when the loop is infinite.
import React from 'react'
import { renderHook } from '@testing-library/react-hooks'
import useBar from './useBar'
describe('Replace `useEffect` with `useLayoutEffect` to check for an infinite loop', () => {
let useEffect = React.useEffect
beforeAll(() => {
React.useEffect = React.useLayoutEffect
})
afterAll(() => {
React.useEffect = useEffect
})
it('useBar', async () => {
const { result } = renderHook(() => useBar())
})
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With