Home > Back-end >  Why does the onChange event handler on <form> pick upp dispatched events from <select>,
Why does the onChange event handler on <form> pick upp dispatched events from <select>,

Time:01-02

I'm building an application in React with many complex forms, and I'm trying to use the onChange event handler on the <form> element.

<form onChange={handleChange}>
  <input name="input1" />
  <input name="input2" />
  ...
</form>

This works great when listening to changes of regular HTML-inputs, but when building a <CustomInput>, I have trouble triggering the onChange on the <form>.

I have created a "hidden" <input> and manually dispatches an Event("change"), but it was not picked up by the onChange on the <form>, however, it does work for the <select> input.

import { useRef } from "react";

function App() {
  const inpRef = useRef();
  const selRef = useRef();

  function onChange(e) {
    console.log(e.target.name); // Only "select" is logged
  }

  function dispatchChange() {
    inpRef.current.dispatchEvent(new Event("change", { bubbles: true }));
    selRef.current.dispatchEvent(new Event("change", { bubbles: true }));
  }
  return (
    <>
      <form onChange={onChange}>
        <input ref={inpRef} name="input" />
        <select ref={selRef} name="select" />
      </form>
      <button onClick={dispatchChange}>Trigger change</button>
    </>
  );
}

export default App;

Why does the "change" event for <select> trigger onChange, but not <input>?

CodePudding user response:

This problem is happening because the "change event" of the input is quiet special. It is managed by "ChangeEventPlugin".

One of the restrictions of this plugin is that the "change" event only will be dispatched if the input value actually changes.

To solve the problem, you have to add this in your code:

useEffect(() => {
    inpRef.current._valueTracker.getValue = () => {
      return 'fake value'
    }
  }, []);

And import "useEffect", of course.

import { useEffect, useRef } from 'react';

So, at the end, the full code will be:

import { useEffect, useRef } from 'react';

function App() {
  const inpRef = useRef();
  const selRef = useRef();

  function onChange(e) {
    console.log(e.target.name); // Only "select" is logged
  }

  function dispatchChange() {
    inpRef.current.dispatchEvent(new Event("change", { bubbles: true }));
    selRef.current.dispatchEvent(new Event("change", { bubbles: true }));
  }

  useEffect(() => {
    inpRef.current._valueTracker.getValue = () => {
      return 'fake value'
    }
  }, []);

  return (
    <>
      <form onChange={onChange}>
        <input ref={inpRef} name="input" />
        <select ref={selRef} name="select" />
      </form>
      <button onClick={dispatchChange}>Trigger change</button>
    </>
  );
}

export default App;

I hope I've helped you. Have a nice day and happy new year!

  • Related