React中利用onFocus和onBlur实现表单字段显隐的问题
解决折叠表单切换内部字段时意外收起的问题
你的核心问题是表单内元素切换焦点时,blur事件先于新元素的focus事件触发,导致误判为表单失焦并收起额外字段。原方案里的setTimeout失效是因为闭包捕获了旧的状态值,无法获取最新焦点状态。
先修正原代码里的两个明显错误:
other_input的onChange逻辑错误,误将值更新到message字段,应该根据name动态更新对应状态- 受控组件未绑定
value属性,导致视图和状态不同步
下面是可行的解决方案,通过ref结合document.activeElement来准确判断焦点是否仍在表单内部:
import { useState, useRef } from 'react'; const defaultFormState = { message: '', other_input: '' }; const FoldableForm = () => { const [formState, setFormState] = useState(defaultFormState); const [isExpanded, setIsExpanded] = useState(false); const formRef = useRef(null); const handleFocus = () => { setIsExpanded(true); }; const handleBlur = () => { // 延迟100ms检查,确保内部元素的focus事件已触发 setTimeout(() => { // 判断当前焦点是否仍在表单范围内 const isFocusInside = formRef.current?.contains(document.activeElement); // 只有当焦点不在表单内,且两个字段都为空时才收起 if (!isFocusInside && !formState.message.trim() && !formState.other_input.trim()) { setIsExpanded(false); } }, 100); }; const handleInputChange = (e) => { const { name, value } = e.target; // 使用函数式更新确保获取最新状态 setFormState(prev => ({ ...prev, [name]: value })); }; return ( <form ref={formRef} onFocus={handleFocus} onBlur={handleBlur} > <textarea name="message" value={formState.message} onChange={handleInputChange} /> {isExpanded && ( <> <input type="text" name="other_input" value={formState.other_input} onChange={handleInputChange} /> <button type="button">Post comment</button> </> )} </form> ); };
方案说明:
- 表单Ref:通过
formRef获取表单DOM元素,用于后续判断焦点是否在表单内部 - 延迟检查焦点:用
setTimeout延迟100ms执行判断逻辑,确保切换焦点时新元素的focus事件已经触发,此时document.activeElement会指向新的输入框 - 焦点范围判断:通过
formRef.current.contains(document.activeElement)确认焦点是否仍在表单内,避免误判 - 状态更新优化:使用函数式更新
setFormState(prev => ...)确保每次更新都基于最新的状态值
这样处理后,在表单内切换输入框时,延迟检查会发现焦点仍在表单内,不会触发收起逻辑;只有当点击表单外部且字段为空时,才会收起额外字段。
内容的提问来源于stack exchange,提问作者kiril
相关产品推荐
相关产品推荐

