reactjs 使用onFocus和onBlur在React中显示隐藏表单字段

tyky79it  于 2022-11-22  发布在  React
关注(0)|答案(2)|浏览(216)

我正在处理一个表单,它最初只显示一个输入字段,当它被聚焦时,它显示其他输入和提交按钮。
我还想隐藏所有那些额外的字段,如果表单在它们为空时失去焦点。这是我无法实现的部分。
这是我的代码:我使用受控窗体和状态来处理焦点。

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在这里是无用的。
我也试着用参考资料,但我不能使它工作。

lh80um4z

lh80um4z1#

在 你 的 例子 中 , 我 认为 使用 shouldShow 状态 是 多余 的 , 你 也 可以 避免 使用 超时 , 这 可能 会 导致 bug 。 你 可以 利用 FocusEvent.relatedTarget 属性 , 防止 隐藏 额外 的 字段 时 , 模糊 从 一 个 输入 和 焦点 到 另 一 个 同时 发生 。
handleOnBlur 函数 应 如下 所 示 :

const handleOnBlur = (e) => {
    if (e.relatedTarget && e.relatedTarget.name === "other_input") return;

    if (!formState.message.trim() && !formState.other_input.trim()) {
      setFocus(false);
    }
  };

中 的 每 一 个
您 可以 在 此 代码 sandbox 中 找到 一 个 工作 示例 。
这种 方法 的 问题 是 , 如果 您 有 多 个 字段 出现 , 您 需要 检查 是否 有 任何 一 个 是 重点 如下 :

["other_input", "another_input"].includes(e.relatedTarget.name)

格式

bvjveswy

bvjveswy2#

这个行为是由于JavaScript中的closureshasFocus的值不是在setTimeout中执行回调时变量的值。它是在执行onBlur回调时的值。
一种解决方案是使用功能更新。
定义一个同时包含hasFocusshouldShow的状态:

const [state, setState] = useState({ hasFocus: false, shouldShow: false });

当您尝试使用功能更新访问以前的状态时,您将获得最新的值:

setTimeout(() => {
    setState((state) => {
        if (!state.hasFocus) {
            return { ...state, shouldShow: false };
        }

        return state;
     });
}, 100);

codesandbox
另一个解决方案是去抖动一个函数,它将hasFocus状态设置为false,这是imo更好的方法。

相关问题