Search code examples
javascriptreactjstypescriptevent-bubbling

Trigger form onChange on input click


I'm creating my custom React component with some extra features. I would like to keep it as close to the default browser behavior as possible. Whenever I change something in the form's input I would like the change event to bubble up to the form. Unfortunately, I can't make it work.

I've read that the "change" event is apparently some special even in React's synthetic event system. None of the proposed solutions worked for me as they were for older React versions.

To simplify my problem I created this demo and what I would like to do is to trigger the form.onChange from within input's onPointerDown or whatever other event handler. Of course I can't call the form.onChange directly as I'm not in the control of this part of the code. Everything should be done through events bubbling. Is it even possible in React? I've tested it in some of the popular UI libraries and so far none of them does that for non-native select components.

export function Demo() {
  return (
    <form
      onChange={(event) => {
        const form = event.currentTarget;
        const formData = new FormData(form);
        console.log(JSON.stringify(Array.from(formData)));
      }}
    >
      <input
        name="name"
        onPointerDown={(event) => {
          const changeEvent = new Event("change", { bubbles: true });
          event.currentTarget.dispatchEvent(changeEvent);
        }}
      />
    </form>
  );
}

Solution

  • Solution 4 : Using Simulate in the Test utilities

    App.js

    import { Simulate } from 'react-dom/test-utils';
    
    export default function Demo() {
      return (
        <form
          onChange={(event) => {
            console.log(`Input Changed simulated : ${event.target.value}`);
          }}
          onClick={(event) => {
            console.log('Input Clicked bubbled up');
          }}
        >
          <input
            name="name"
            onClick={(e) => {
              Simulate.change(e.target.form, { target: { value: e.target.value } });
            }}
          />
        </form>
      );
    }
    

    Test run:

    Clicked on the input element after typing the text 'some test data'.

    console log generated as:

    // Input Changed simulated : some test data
    // Input Clicked bubbled up
    

    Observation:

    The first log entry has been created here by the event simulation. The user has clicked on the input element, and the onClick event hander in the input element has the code to simulate the event into a input changed event in the form element. Please note that the reference e.target.form is a generic one, it does not hard code anything here.

    The second log entry is the result of the default event bubbling.