如何正确控制 React-Bootstrap InputGroup 上的焦点和模糊事件?

How to properly control focus and blur events on a React-Bootstrap InputGroup?

我有一个来自 react-bootstrap 的输入元素,它允许用户更改字段值,并且会出现 2 个按钮,一个用于接受更改,另一个用于取消更改,保留原始内容值。

我通过将侦听器委托给包装组件来设法控制焦点和模糊事件,我的想法是,由于焦点仍在包装组件内,我不会失去焦点,但按下内部按钮似乎模糊了焦点,因此接受和取消按钮不会触发任何事件...

这是我的代码示例:

import { useState, useEffect, useRef } from "react";
import { InputGroup, Button, FormControl } from "react-bootstrap";

import "./styles.css";

const InputField = ({ title }) => {
  const formRef = useRef(null);
  const [value, setValue] = useState(title);
  const [toggleButtons, setToggleButtons] = useState(false);

  const onChange = (e) => {
    setValue(e.target.value);
  };

  const onFocus = () => {
    setToggleButtons(true);
  };

  const onBlur = () => {
    setToggleButtons(false);
  };

  const acceptChange = () => {
    console.log("Accept");
    setToggleButtons(false);
  };

  const cancelChange = () => {
    console.log("Cancel");
    setToggleButtons(false);
  };

  useEffect(() => {
    const form = formRef.current;
    form.addEventListener("focus", onFocus);
    form.addEventListener("blur", onBlur);

    return () => {
      form.removeEventListener("focus", onFocus);
      form.removeEventListener("blur", onBlur);
    };
  }, []);

  return (
    <div className="App">
      <InputGroup className="m-3" style={{ width: "400px" }}>
        <FormControl
          ref={formRef}
          value={value}
          onChange={onChange}
          // onFocus={onFocus}
          // onBlur={onBlur}
        />
        {toggleButtons ? (
          <InputGroup.Append>
            <Button variant="outline-secondary" onClick={() => acceptChange()}>
              Accept
            </Button>
            <Button variant="outline-secondary" onClick={() => cancelChange()}>
              Cancel
            </Button>
          </InputGroup.Append>
        ) : null}
      </InputGroup>
    </div>
  );
};

export default function App() {
  return (
    <>
      <InputField title={"Input 1"} />
      <InputField title={"Input 2"} />
      <InputField title={"Input 3"} />
      <InputField title={"Input 4"} />
    </>
  );
}

需要进行一些更改才能使此工作正常进行:

  1. 切换按钮需要始终在 DOM 中,因此隐藏它们而不是仅在焦点存在时才渲染。
  2. 为了避免在从输入到其中一个按钮发生模糊时隐藏按钮,您可以使用事件的 relatedTarget 和 [=11 检查新聚焦的元素是否是输入的兄弟元素=].

例如:

import { useState } from "react";
import { InputGroup, Button, FormControl } from "react-bootstrap";

import "./styles.css";

const InputField = ({ title }) => {
  const [value, setValue] = useState(title);
  const [toggleButtons, setToggleButtons] = useState(false);

  const onChange = (e) => {
    setValue(e.target.value);
  };

  const onFocus = () => {
    setToggleButtons(true);
  };

  const onBlur = (e) => {
    if (!e.currentTarget.parentNode.contains(e.relatedTarget)) {
      setToggleButtons(false);
    }
  };

  const acceptChange = () => {
    console.log("Accept");
    setToggleButtons(false);
  };

  const cancelChange = () => {
    console.log("Cancel");
    setToggleButtons(false);
  };

  return (
    <div className="App">
      <InputGroup className="m-3" style={{ width: "400px" }}>
        <FormControl
          value={value}
          onChange={onChange}
          onFocus={onFocus}
          onBlur={onBlur}
        />
        <InputGroup.Append className={toggleButtons ? "d-flex" : "d-none"}>
          <Button
            onBlur={onBlur}
            variant="outline-secondary"
            onClick={() => acceptChange()}
          >
            Accept
          </Button>
          <Button
            onBlur={onBlur}
            variant="outline-secondary"
            onClick={() => cancelChange()}
          >
            Cancel
          </Button>
        </InputGroup.Append>
      </InputGroup>
    </div>
  );
};

export default function App() {
  return (
    <>
      <InputField title={"Input 1"} />
      <InputField title={"Input 2"} />
      <InputField title={"Input 3"} />
      <InputField title={"Input 4"} />
    </>
  );
}

https://codesandbox.io/s/input-group-focus-slwoh