5

I'm trying to build an input component with a clear button using react@17

import { useRef } from 'react';

const InputWithClear = props => {
  const inputRef = useRef();
  return (
    <div>
      <input
        ref={inputRef}
        {...props}
      />
      <button
        onClick={() => {
          inputRef.current.value = '';
          inputRef.current.dispatchEvent(
            new Event('change', { bubbles: true })
          );
        }}
      >
        clear
      </button>
    </div>
  );
};

using this component like:

<InputWithClear value={value} onChange={(e) => {
  console.log(e); // I want to get a synthetic event object here
}} />

but the clear button works once only when I did input anything first, and stop working again.

if I input something first and then click the clear button, it does not work.

why not using?

<button
  onClick={() => {
    props.onChange({
      target: { value: '' }
    })
  }}
>
  clear
</button>

because the synthetic event object will be lost

So, how do I manually trigger a synthetic change event of a react input component?

Littlee
  • 3,791
  • 6
  • 29
  • 61
  • 1
    See [this StackOverflow answer](https://stackoverflow.com/a/46012210/2270233). React seems to override the element's value setter. Calling the browser's value setter and then dispatching the event seems to work. – daniels May 12 '22 at 08:11

2 Answers2

0

you should use state to control input value rather than create useRef, that's the way to go. you can use a stopPropagation prop to control it:

const InputWithClear = ({value, setValue, stopPropagation = false}) => {

  const onClick = (e) => {
    if(stopPropagation)  e.stopPropagation()
    setValue('')
  }

  return (
    <div>
      <input
        value={value}
        onChange={e => setValue(e.target.value)}
      />
      <button
        onClick={onClick}
      >
        clear
      </button>
    </div>
  );
};

export default function App() {
  const [value, setValue] = useState('')
  return (
    <div className="App">
      <InputWithClear value={value} setValue={setValue} stopPropagation />
    </div>
  );
}
buzatto
  • 9,704
  • 5
  • 24
  • 33
  • I've updated my question, in my situation, `onChange` is passed from outside components, and I want to make `` can be used like an ``, such as it can decide whether to call `e.stopPropagation()` or not – Littlee Dec 05 '20 at 06:46
  • right, I edited my answer to fit your needs. You could use a `stopPropagation` prop flag (default false) to control it. – buzatto Dec 05 '20 at 20:05
0

Try this approach,

Maintain state at the parent component level (Here parent component is App), onClear, bubble up the handler in the parent level, and update the state.

import React, { useState } from "react";
import "./styles.css";

const InputWithClear = (props) => {
  return (
    <div>
      <input {...props} />
      <button onClick={props.onClear}>clear</button>
    </div>
  );
};

export default function App() {
  const [value, setValue] = useState("");
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <InputWithClear
        value={value}
        onChange={(e) => {
          console.log(e); // I want to get a synthetic event object here
          setValue(e.target.value);
        }}
        onClear={() => {
          setValue("");
        }}
      />
    </div>
  );
}

Working code - https://codesandbox.io/s/youthful-euler-gx4v5?file=/src/App.js

Sarun UK
  • 6,210
  • 7
  • 23
  • 48