Search code examples
javascriptreactjseventsevent-handlingonchange

How to generate/trigger an React.ChangeEvent<HTMLInputElement> with empty value on input


I'm building a custom React Input (using Chakra-UI) who can be cleaned using an Icon button (when you click on the Button, the value is updated to "").

The issue is, I have to propagate an React.ChangeEvent<HTMLInputElement> event for onChange function (to be able to be used as any ChakraUI Input).

Frist Try: Build a new Event from nothing.

For now this is my Component, but I have no idea how to generate a new onChange event on the button onClick event.

import React, { useState } from "react"
import { IconButton, InputGroup, InputGroupProps, InputProps, InputRightElement, Input } from "@chakra-ui/react"
import { MdClose } from "react-icons/md";

export type CleanableInputProps = InputProps & { 
  Input?: (props: InputProps) => JSX.Element,
  inputGroupProps?: InputGroupProps,
}

export const CleanableInput = ({
  inputGroupProps = {}, // Allow to inject props on wrapper
  ...props
}: CleanableInputProps): JSX.Element => {

  const [search, setSearch] = useState<CleanableInputProps['value']>(props?.value);
  const _setSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSearch(e.currentTarget.value); // Update React value
    props.onChange?.(e);
  }

  return (
    <InputGroup {...inputGroupProps}>
      <Input {...props} value={search} onChange={_setSearch} />
      <InputRightElement>
        <IconButton  icon={<MdClose />} aria-label="clean" onClick={() => {

          //
          // Create event Input onChange event with value=""
          // e = __________________;
          //

          _setSearch(e);

        }}/>
      </InputRightElement>
    </InputGroup>
  )
}

Second try: Update by ref the input and trigger input onChange

I did another version using input ref, and updating the input value then trigger the event but:

  • Now I have issue using another component then ChakraUI input
  • The input value is well updated (visually it's blank after clicking) but the onChange is never triggered.
import React, { useRef } from "react"
import { IconButton, InputGroup, InputGroupProps, InputProps, InputRightElement, Input } from "@chakra-ui/react"
import { MdClose } from "react-icons/md";

export type CleanableInputProps = InputProps & {
  inputGroupProps?: InputGroupProps,
}

export const CleanableInput = ({
  inputGroupProps = {}, // Allow to inject props on wrapper
  ...props
}: CleanableInputProps): JSX.Element => {

  const inputRef = useRef<HTMLInputElement | null>(null);

  return (
    <InputGroup {...inputGroupProps}>
      <Input {...props} ref={inputRef} value={props.value} />
      <InputRightElement>
        <IconButton icon={<MdClose />} aria-label="" onClick={() => {
          if (inputRef != undefined && inputRef.current != undefined) {
            // Update value (working)
            inputRef.current.value = "";

            // Trigger onChange event manually (Not working)
            let event = new Event('input', { bubbles: true });
            inputRef.current.dispatchEvent(event);
          }
        }} />
      </InputRightElement>
    </InputGroup>
  )
}

Solution

  • After days of looking for answer, look like the way I updated the value into the referred input wasn't good, so the event was well trigger but didn't see any update (so was not propagated)

    This is working good:

      // Update the value by ref
      var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")!.set;
      nativeInputValueSetter!.call(inputRef.current, '');
    
      // Then trigger the onChange event of the input 
      //    for some input (select, textarea) we may want to trigger 'change' instead of 'input' who not listen to input event
      inputRef.current.dispatchEvent(new Event('input', { bubbles: true }));
    

    Complete solution:

    import React, { useRef } from "react"
    import { IconButton, InputGroup, InputGroupProps, InputProps, InputRightElement, Input } from "@chakra-ui/react"
    import { MdClose } from "react-icons/md";
    
    export type CleanableInputProps = InputProps & {
      inputGroupProps?: InputGroupProps,
      triggerEventName?: 'input' | 'change',
    }
    
    export const CleanableInput = ({
      inputGroupProps = {}, // Allow to inject props on wrapper
      triggerEventName = 'input',
      ...props
    }: CleanableInputProps): JSX.Element => {
    
      const inputRef = useRef<HTMLInputElement | null>(null);
    
      return (
        <InputGroup {...inputGroupProps}>
          <Input {...props} ref={inputRef} value={props.value} />
          <InputRightElement>
            <IconButton icon={<MdClose />} aria-label="" onClick={() => {
              if (inputRef != undefined && inputRef.current != undefined) {
                // Update the value by ref
                var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")!.set;
                nativeInputValueSetter!.call(inputRef.current, '');
    
                // Then trigger the onChange event of the input 
                //    for some input (select, textarea) we may want to trigger 'change' instead of 'input' who not listen to input event
                inputRef.current.dispatchEvent(new Event(triggerEventName, { bubbles: true }));
              }
            }} />
          </InputRightElement>
        </InputGroup>
      )
    }