Created
October 26, 2025 11:27
-
-
Save FaviusTy/0515b7fa8a2fa3860bf3390335ba4994 to your computer and use it in GitHub Desktop.
MicrosoftIME利用時の未確定入力値の不正挿入の対応実装
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { memo, useCallback, useState } from "react"; | |
| export const Input = memo(function Input() { | |
| const [composedValue, setComposedValue] = useState(""); | |
| const InputHandle = useCallback( | |
| (e: React.InputEvent<HTMLInputElement>) => { | |
| const { target } = e; | |
| const { data, isComposing, inputType } = e.nativeEvent; | |
| console.log( | |
| "InputHandle", | |
| "inputType:", | |
| inputType, | |
| "value:", | |
| target.value, | |
| "data:", | |
| data, | |
| "isComposing:", | |
| isComposing, | |
| "selectionStart:", | |
| target.selectionStart, | |
| "selectionEnd:", | |
| target.selectionEnd, | |
| "composedValue:", | |
| composedValue | |
| ); | |
| //MSIMEは変換確定せずにfocusoutした時にIMEの入力値が二重になってしまうため、composeEnd時に確保していた値で上書きする | |
| if ( | |
| composedValue && | |
| inputType !== "deleteContentBackward" && //元の入力値次第でcomposeEnd直後のinsertTextの間にdeleteContentBackwardが入ることがあるため除外する | |
| inputType !== "insertCompositionText" //insertCompositionTextはIMEモード中の入力値操作のため、ここも除外する | |
| ) { | |
| console.log("set composedValue:", composedValue); | |
| //NOTE: 直近入力値が半角数字の場合はEnterによる確定操作直後の可能性が高いため、composedValueをクリアするだけでvalueの上書きを行わない | |
| if (/^[0-9]+$/.test(data)) { | |
| return setComposedValue(""); | |
| } | |
| e.target.value = composedValue; | |
| setComposedValue(""); | |
| } | |
| }, | |
| [composedValue, setComposedValue] | |
| ); | |
| const FocusInHandle = useCallback( | |
| (e: React.FocusEvent<HTMLInputElement>) => { | |
| setComposedValue(""); | |
| e.target.select(); | |
| const { target } = e; | |
| console.log( | |
| "FocusInHandle", | |
| "value:", | |
| target.value, | |
| "selectionStart:", | |
| target.selectionStart, | |
| "selectionEnd:", | |
| target.selectionEnd | |
| ); | |
| }, | |
| [setComposedValue] | |
| ); | |
| const FocusOutHandle = useCallback( | |
| (e: React.FocusEvent<HTMLInputElement>) => { | |
| const { target } = e; | |
| console.log( | |
| "FocusOutHandle", | |
| "value:", | |
| target.value, | |
| "selectionStart:", | |
| target.selectionStart, | |
| "selectionEnd:", | |
| target.selectionEnd | |
| ); | |
| //IMEモード中、きちんとEnterで変換確定操作を実施すると、onInputイベントは発生しないため、ここで確保していた値で上書きする | |
| if (composedValue) { | |
| console.log("set composedValue on blur:", composedValue); | |
| e.target.value = composedValue; | |
| } | |
| setComposedValue(""); | |
| }, | |
| [composedValue, setComposedValue] | |
| ); | |
| const CompositionStartHandle = useCallback( | |
| (e: React.CompositionEvent<HTMLInputElement>) => { | |
| const { target } = e; | |
| console.log( | |
| "CompositionStartHandle", | |
| "value:", | |
| target.value, | |
| "selectionStart:", | |
| target.selectionStart, | |
| "selectionEnd:", | |
| target.selectionEnd | |
| ); | |
| }, | |
| [] | |
| ); | |
| const CompositionEndHandle = useCallback( | |
| (e: React.CompositionEvent<HTMLInputElement>) => { | |
| const { target } = e; | |
| console.log( | |
| "CompositionEndHandle", | |
| "value:", | |
| target.value, | |
| "selectionStart:", | |
| target.selectionStart, | |
| "selectionEnd:", | |
| target.selectionEnd | |
| ); | |
| setComposedValue(target.value); | |
| }, | |
| [setComposedValue] | |
| ); | |
| return ( | |
| <input | |
| defaultValue={1234} | |
| onInput={InputHandle} | |
| onFocus={FocusInHandle} | |
| onBlur={FocusOutHandle} | |
| onCompositionStart={CompositionStartHandle} | |
| onCompositionEnd={CompositionEndHandle} | |
| /> | |
| ); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment