我正在处理一个表单,它最初只显示一个输入字段,当它被聚焦时,它显示其他输入和提交按钮。
我还想隐藏所有那些额外的字段,如果表单在它们为空时失去焦点。这是我无法实现的部分。
这是我的代码:我使用受控窗体和状态来处理焦点。
const FoldableForm = () => {
const [formState, setFormState] = useState(defaultFormState);
const [hasFocus, setFocus] = useState(false);
const handleOnBlur = () => {
if (!formState.message.trim() && !formState.other_input.trim()) {
setFocus(false);
}
};
return (
<form
onFocus={() => setFocus(true)}
onBlur={handleOnBlur}
>
<textarea
name="message"
onChange={(e) => setFormState({ ...formState, message: e.target.value })}
/>
{hasFocus && (
<>
<input
type="text" name="other_input"
onChange={(e) => setFormState({ ...formState, message: e.target.other_input })}
/>
<button type="button">Post comment</button>
</>
)}
</form>
);
}
目前,如果我在文本区域输入内容,setFocus(false)
不会被调用,所以它可以正常工作。否则,如果我将其留空并单击另一个输入字段,handleOnBlur
函数将被调用,它将焦点设置为false,因此表单被“最小化”。
这是意料之中的,因为blur事件(来自textarea)在focus事件(来自新的输入字段)之前触发,所以我尝试使用setTimeout来检查,在几分之一秒之后,focus事件是否已经发生。
为此,我使用了第二个状态(shouldShow),它在handleOnBlue函数的setTimeout中更新。
setTimeout(() => {
if(!hasFocus) {
setShouldShow(false); // this should cause the form to minimize
}
}, 100);
然而,根据react生命周期,传递给setTimeout函数的hasFocus的值是在调用时,而不是在执行时,所以setTimeout在这里是无用的。
我也试着用参考资料,但我不能使它工作。
2条答案
按热度按时间lh80um4z1#
在 你 的 例子 中 , 我 认为 使用
shouldShow
状态 是 多余 的 , 你 也 可以 避免 使用 超时 , 这 可能 会 导致 bug 。 你 可以 利用 FocusEvent.relatedTarget 属性 , 防止 隐藏 额外 的 字段 时 , 模糊 从 一 个 输入 和 焦点 到 另 一 个 同时 发生 。handleOnBlur
函数 应 如下 所 示 :中 的 每 一 个
您 可以 在 此 代码 sandbox 中 找到 一 个 工作 示例 。
这种 方法 的 问题 是 , 如果 您 有 多 个 字段 出现 , 您 需要 检查 是否 有 任何 一 个 是 重点 如下 :
格式
bvjveswy2#
这个行为是由于JavaScript中的closures。
hasFocus
的值不是在setTimeout
中执行回调时变量的值。它是在执行onBlur
回调时的值。一种解决方案是使用功能更新。
定义一个同时包含
hasFocus
和shouldShow
的状态:当您尝试使用功能更新访问以前的状态时,您将获得最新的值:
codesandbox
另一个解决方案是去抖动一个函数,它将
hasFocus
状态设置为false,这是imo更好的方法。