downshift-js / downshift-js/downshift
Japanese IME not working when controlling `inputValue` in `useCombobox`
- Dominant language
- JavaScript
- Stars
- 12.3k
- Forks
- 936
- PR merge metrics
- No merged PRs in 30d
Description
- `downshift` version: 6.1.12
- `node` version: 14.19.0
- `yarn` version: 1.22.17
**Relevant code or config**
```tsx
type Book = { author: string; title: string };
const books: Book[] = [
{ author: 'Harper Lee', title: 'To Kill a Mockingbird' },
{ author: 'Lev Tolstoy', title: 'War and Peace' },
{ author: 'Fyodor Dostoyevsy', title: 'The Idiot' },
{ author: 'Oscar Wilde', title: 'A Picture of Dorian Gray' },
{ author: 'George Orwell', title: '1984' },
{ author: 'Jane Austen', title: 'Pride and Prejudice' },
{ author: 'Marcus Aurelius', title: 'Meditations' },
{ author: 'Fyodor Dostoevsky', title: 'The Brothers Karamazov' },
{ author: 'Lev Tolstoy', title: 'Anna Karenina' },
{ author: 'Fyodor Dostoevsky', title: 'Crime and Punishment' },
];
function getBooksFilter(inputValue: string | undefined) {
return function booksFilter(book: Book) {
return (
!inputValue ||
book.title.toLowerCase().includes(inputValue) ||
book.author.toLowerCase().includes(inputValue)
);
};
}
export const Example = () => {
const [items, setItems] = React.useState(books);
const [inputValue, setInputValue] = React.useState('');
const {
isOpen,
getToggleButtonProps,
getLabelProps,
getMenuProps,
getInputProps,
getItemProps,
getComboboxProps,
} = useCombobox({
onInputValueChange(chg) {
setInputValue(chg.inputValue || '');
setItems(books.filter(getBooksFilter(chg.inputValue)));
},
inputValue, // Controlling inputValue here introduces IME composition issue
items,
itemToString(item) {
return item ? item.title : '';
},
});
return (
Choose your favorite book:
{isOpen ? <>↑ : <>↓}
-
{item.title}
{item.author}
{isOpen &&
items.map((item, index) => (
))}
);
};
```
**What you did**:
When entering Japanese (or any IME language) into a combobox that controls inputValue, IME composition fails.
This first screenshot renders a dropdown with `inputValue` controlled. I clicked the input to focus it and then typed "j" + "a".

**What happened**:
The IME editor failed to open to further compose the characters before committing them. What I should have saw would have been this:
Additionally, it appears that the `compositionend` event is never fired when inputValue is controlled.
**Problem description**:
When useCombobox is controlling `inputValue` and updating it with `onInputValueChange`, IME composition exits before showing the composition editor.
**Suggested solution**:
Wrap getInputProps's onChange handler with the spirit of the following:
```tsx
const onComposition = useRef(false);
const getOnChangeWithCompositionSupport = useCallback(
({
onChangeProp,
}: {
onChangeProp: (e: ChangeEvent) => void;
}) =>
(event: ChangeEvent) => {
setInputValue(event?.target?.value);
// IME method start
if (event.type === 'compositionstart') {
onComposition.current = true;
return;
}
// IME method end
if (event.type === 'compositionend') {
onComposition.current = false;
}
// handle parent onChange
if (!onComposition.current) {
onChangeProp?.(event);
}
},
[setInputValue]
);
```
Contributor guide
Assessment
This issue has not been assessed yet.