Skip to content

Instantly share code, notes, and snippets.

@kamikat
Last active December 27, 2022 03:50
Show Gist options
  • Save kamikat/ca34166e2af6617d9b29a397b2e51c6f to your computer and use it in GitHub Desktop.
Save kamikat/ca34166e2af6617d9b29a397b2e51c6f to your computer and use it in GitHub Desktop.
Workaround composition event problem in controlled React input components.
import { useEffect, useState, useRef } from "react";
export function useAsyncInputRef<T extends HTMLElement & { value: string }>(
value: string,
forwardRef?: React.RefObject<T>
) {
const [compositionFlag, setComposition] = useState(false);
const newRef = useRef<T>(null);
const ref = forwardRef || newRef;
useEffect(() => {
const el = ref.current;
if (el) {
const compositionStart = () => setComposition(true);
const compositionEnd = () => setComposition(false);
el.addEventListener("compositionstart", compositionStart);
el.addEventListener("compositionend", compositionEnd);
return () => {
el.removeEventListener("compositionstart", compositionStart);
el.removeEventListener("compositionend", compositionEnd);
setComposition(false);
};
}
}, [ref]);
useEffect(() => {
if (ref.current && compositionFlag === false) {
ref.current.value = value;
}
}, [compositionFlag, value, ref]);
return ref;
}
@kamikat
Copy link
Author

kamikat commented Dec 27, 2022

render(
  <input
    type="text"
    ref={useAsyncInputRef(value)}
    onChange={(e) => setValue(e.target.value)}
  />
);

Live demo: https://codepen.io/kamikat/pen/OJwMJLr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment