React自定义InputCode组件onValueSet回调中调用focus方法失效问题
问题原因
- 浏览器按键事件的默认行为冲突:
onKeyPress事件触发时,浏览器默认会将焦点绑定在当前触发输入的输入框上,你在onKeyPress的同步流程中手动切换焦点后,后续的浏览器默认行为会把焦点重新拉回当前输入框,导致聚焦失效。而onKeyDown的触发时机早于浏览器的焦点绑定逻辑,所以相同逻辑放在onKeyDown中可以正常执行。 - React批量更新机制的影响:你在
onKeyPress中同时触发了InputCode内部的状态更新setValueState和react-hook-form的表单状态更新,React18+会对事件回调中的所有状态更新做批量合并,等整个事件回调执行完成后才会重渲染DOM,你同步调用focus()时DOM可能还未完成最新状态的挂载,也会导致聚焦失效。
解决方法
方法1:将聚焦逻辑推迟到宏任务队列执行
用setTimeout把聚焦逻辑包装,等当前所有同步任务(包括React重渲染、浏览器默认事件行为)执行完成后再运行,修改handleOnValueSet即可:
const handleOnValueSet = (index: number) => { const formvalues = getValues(); onCodeChange(formvalues); if (index + 1 < CODE_INPUTS.length) { setTimeout(() => { inputListRefs.current[index + 1].current?.focus(); }, 0) } }
方法2:阻止onKeyPress的默认行为
在InputCode的handleKeyPress中,确认输入是有效数字后,调用event.preventDefault()阻止浏览器默认的焦点保持行为:
const handleKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => { const value: number = parseInt(event.key, 10); if (!isNaN(value)) { event.preventDefault(); // 新增该行阻止默认行为 const valueString: string = value.toString(); setValueState(valueString); onChange(valueString); onValueSet(valueString); } };
方法3:通过useEffect监听值变化触发聚焦
单独监听表单值的变化,值更新后自动触发下一个输入框的聚焦,完全和事件时序解耦:
useEffect(() => { const lastFilledIndex = CODE_INPUTS.findLastIndex(input => getValues(input.name)?.length > 0); if (lastFilledIndex > -1 && lastFilledIndex + 1 < CODE_INPUTS.length) { inputListRefs.current[lastFilledIndex + 1].current?.focus(); } }, [getValues()])
内容的提问来源于stack exchange,提问作者Steve Rogers
相关产品推荐
相关产品推荐

