Search code examples
reactjsreact-testing-libraryreact-hook-form

handleSubmit in react-hook-form not allowing state changes


Using react-testing-library I am doing a very simple test : just rendering a component and clicking on a button.

it('tests a button click', async () => {

    render(<Test/>)

    await screen.findAllByRole('heading')

    let buttons = await screen.findAllByRole("button");
    fireEvent.click(buttons[0]);
});

My component "Test" is very simple, containing just a heading and form with a button.

The form uses react-hook-form, and I would like to react to the submitting of the form. I would like to change the value of my "foobar" object.

However in the following code the "console.info" will never be called.

export default function Test() {

    const { handleSubmit } = useForm();

    const [foobar, setFoobar] = React.useState<string>("foo");

    if (foobar) {
        console.info("this will never get called if onSubmit is called from handleSubmit")
    }

    const onSubmit = () => {
        setFoobar("bar")
    }
    return (
        <>
            <h1>Test</h1>
            <form onSubmit={handleSubmit(onSubmit)}>
                <button type={"submit"}>Hi</button>
            </form>
        </>
    );
}

I do not understand this behaviour, because if I just have a normal button like in the code below, the "console.info" WILL be called.

export default function Test() {

    const { handleSubmit } = useForm();

    const [foobar, setFoobar] = React.useState<string>("foo");

    if (foobar) {
        console.info("this will never get called if onSubmit is called from handleSubmit")
    }

    const onSubmit = () => {
        setFoobar("bar")
    }
    return (
        <>
            <h1>Test</h1>
            <form onSubmit={handleSubmit(onSubmit)}>
            </form>
            <button onClick={()=>onSubmit()}>Hi</button>
        </>
    );
}

Strangely, this seems to only happen when I use "react testing library".

If I use both these examples in my actual application then BOTH "console.info" will be called.

So, at this stage I am not sure if the problem lies with react-hook-form or with react-testing-library.

Any help much appreciated.


Solution

  • If you wrap the fireEvent call inside an act(...) it should work:

    it('tests a button click', async () => {
    
      render(<Test/>)
    
      await screen.findAllByRole('heading');
    
      let buttons = await screen.findAllByRole('button');
    
      await act(async () => {
        fireEvent.click(buttons[0]);
      });
    
    });