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).
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>
)
}
I did another version using input ref, and updating the input value then trigger the event but:
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>
)
}
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>
)
}