一旦完結はしたんだけど チューニング関係でやっぱり change イベントがほしいなと思ったので
一文字入力ごとに state に同期させると input にテキスト入力するときにかなりの頻度で再レンダリングされる
タイプが早い人だと 1 秒間に何回も
input の再レンダリングだけなら何の問題もない程度だけど input の value に指定する state は親の親の親とか input より親のコンポーネントから渡されるのがほとんど
ページやアプリのルートに近いコンポーネントで管理してることが多い
そうなると 再レンダリング対象コンポーネントが多くなるし 次の文字入力時にまだ処理中ってこともある
処理が重いコンポーネントは作ったときにメモするようにしておけばいいけど 毎回それも面倒だし チューニングは遅いと感じたらする方針にしてることが多い
それでいざやるときにはあちこちのコンポーネントをメモ化することになってやる気が起きない
問題なのは再レンダリング頻度が異常に高い input の方なんだからこっちをどうにかすべきだと思う
ただ文字入力ごとに状態が変更されてるわけだし できることはやっぱり change イベントにして入力が一旦終わったところで反映するくらい
一文字ごとに状態を更新するメリットって入力途中なものをどこかに反映したり 入力チェックしたりできるくらいで あまり重要じゃないと思う
markdown エディタ兼プレビューツールみたいな input(textarea) からフォーカスを外さないようなのだと change イベントじゃだめだけど input イベントで毎回更新が必要というわけではなくて 変更があった場合に数秒に 1 回くらいの更新でいい
入力チェックは入力中にあれこれ言われたくなくて入力後にしてほしいって言う話を聞くことがあるし 私もどちらかというとその意見に賛成
なので input をラップするコンポーネント内では input イベントごとに state へ反映してもその外側のアプリケーションとしては change イベントなどのタイミングで更新するくらいで十分
こういう Input コンポーネントを用意しておくと良いかも
const Input = ({ value, onChange: notifyChange, ...props }) => {
const [state, setState] = useState("")
useEffect(() => {
if (state !== value) {
setState(value)
}
}, [value])
const onBlur = () => {
if (state !== value && notifyChange) {
notifyChange(state)
}
}
const onChange = (event) => {
setState(event.target.value)
}
return <input {...props} value={state} onChange={onChange} onBlur={onBlur} />
}
const App = () => {
const [state, setState] = useState("aa")
const onChange = (value) => {
console.log("changed")
setState(value)
}
return <Input value={state} onChange={onChange} />
}
二重に保持はしてるけど App コンポーネントにはフォーカス外れたときに反映される