Emulating DOM onchange behavior in React

Viewed 143

I'm trying to emulate standard DOM onchange event behavior in React. The React onChange event acts like the DOM oninput event, firing every time a key entry is made, rather than when the entry is complete. This makes verifying input difficult unless you use a separate submit button (which I don't). I believe this to be a bug in React.

So far, I've discovered the following techniques come close:

  • Emulate submit on loss of focus via an onBlur event handler that submits the value when the focus is lost.
  • Emulate submit on hitting the return/enter key via an onKeyUp event handler that submits the entry when evt.key is "Enter" or "Accept".
  • For numeric input, emulating submit on increment/decrement (Safari input up/down arrows, up/down keys, up/down scroll wheel) by having the onChange handler detect when the change in value is equal to the step and being careful to compare within a reasonable epsilon for non-integer steps. Number.EPSILON was too small for steps of 0.1, so I had to use something bigger (but still very small). This only approximates the DOM behavior: If a user selects the last digit and provides a digit that changes the value by the step, it will also submit when the digit is entered.

Am I missing any cases? Is there a better technique?

2 Answers

You should use Debounce method for such a functionality. You can pass the event handler function within the debounce method and a delay (in milliseconds).

So when you are typing a text and you want to call the event handler after the user has typed. Using the debounce function, the event handler will be called after a delay after the user stopped typing.

Edit:

You can read more about it from here - link

This is not a recommended approach, BUT, if you have to do something such a custom hook, it could be like this (pretty basic though):

Notice I'm using defaultValue instead of value because native 'change' event doesn't trigger as you type.

function useOnChange(handleOnChange) {
  const ref = React.createRef(null);
  
  React.useEffect(() => {
     ref.current.addEventListener('change', handleOnChange);
  }, []);
  
  return ref;
}

function App() {
  const [value, setValue] = React.useState(0);
  const onChangeRef = useOnChange((e) => {
    console.log('on change', e.target.value)
    setValue(e.target.value);
  });
  
  return (
    <div className="App">
      <input type="number" defaultValue={value} ref={onChangeRef} />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related