React-testing-library: Second controlled input doesn't update state

Created on 8 Nov 2019  路  3Comments  路  Source: testing-library/react-testing-library

  • react-testing-library version: 9.3.2
  • react version: latest
  • node version: .12.12.0
  • npm (or yarn) version 1.16.0 yarn:

What you did:

Just simple form with 2 text inputs.
In the test file, I triggered 2 fireEvent.change functions inside act.

What happened:

Only the first input field value was updated.
State become {first: "1", second: ""}
but it shoud be {first: "1", second: "2"}

Reproduction:

https://codesandbox.io/s/2-controlled-inputs-3f13h

Most helpful comment

Hi @kentcdodds I am still facing the same issue. When I am changing the input value using fireEvent, only the first input gets updated. Could you please help me.
Form.tsx

import React, { useState } from 'react';

export default function Form(props: { onSubmit: (values: object) => void }) {
    const [state, setState] = useState({ firstName: '', lastName: '' });

    const handleChange = (event: React.FormEvent<HTMLInputElement>) => {
            const element = event.target as HTMLInputElement;
            setState(st => ({ ...st, [element.name]: element.value }));
        },
        handleSubmit = (event: React.FormEvent) => {
            event.preventDefault();
            props.onSubmit && props.onSubmit(state);
        };

    return (
        <form onSubmit={handleSubmit} id="formId">
            <input id="firstName" name="firstName" value={state.firstName} onChange={handleChange} />
            <input id="lastName" name="lastName" value={state.lastName} onChange={handleChange} />
            <button type="submit" form="formId">
                Submit
            </button>
        </form>
    );
}

Form.test.tsx

import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import Form from './Form';

describe('App', () => {
    it('should call onSubmit with expected data', () => {
        const mockOnSubmit = jest.fn(),
            { container } = render(<Form onSubmit={mockOnSubmit} />);

        fireEvent.change(container.querySelector('#firstName'), {
            target: { value: 'John' }
        });
        fireEvent.change(container.querySelector('#lastName'), {
            target: { value: 'Shinde' }
        });
        fireEvent.submit(container.querySelector('form'));
        expect(mockOnSubmit).toBeCalledWith({
            firstName: 'John',
            lastName: 'Shinde'
        });
    });
});

All 3 comments

However if you editing line 9 at demo.js file to
const [state, setState] = React.useState({});
Everything works as expected except that console.error about changing uncontrolled to controlled component

I'm not certain why, but the use of event persistence was causing the issue. I also made it so you don't need act because RTL does this automatically for you. https://codesandbox.io/s/2-controlled-inputs-x6ku7

Hi @kentcdodds I am still facing the same issue. When I am changing the input value using fireEvent, only the first input gets updated. Could you please help me.
Form.tsx

import React, { useState } from 'react';

export default function Form(props: { onSubmit: (values: object) => void }) {
    const [state, setState] = useState({ firstName: '', lastName: '' });

    const handleChange = (event: React.FormEvent<HTMLInputElement>) => {
            const element = event.target as HTMLInputElement;
            setState(st => ({ ...st, [element.name]: element.value }));
        },
        handleSubmit = (event: React.FormEvent) => {
            event.preventDefault();
            props.onSubmit && props.onSubmit(state);
        };

    return (
        <form onSubmit={handleSubmit} id="formId">
            <input id="firstName" name="firstName" value={state.firstName} onChange={handleChange} />
            <input id="lastName" name="lastName" value={state.lastName} onChange={handleChange} />
            <button type="submit" form="formId">
                Submit
            </button>
        </form>
    );
}

Form.test.tsx

import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import Form from './Form';

describe('App', () => {
    it('should call onSubmit with expected data', () => {
        const mockOnSubmit = jest.fn(),
            { container } = render(<Form onSubmit={mockOnSubmit} />);

        fireEvent.change(container.querySelector('#firstName'), {
            target: { value: 'John' }
        });
        fireEvent.change(container.querySelector('#lastName'), {
            target: { value: 'Shinde' }
        });
        fireEvent.submit(container.querySelector('form'));
        expect(mockOnSubmit).toBeCalledWith({
            firstName: 'John',
            lastName: 'Shinde'
        });
    });
});
Was this page helpful?
0 / 5 - 0 ratings