React 中的 onKeyDown / onKeyUp 侦听器

onKeyDown / onKeyUp listener in React

我正在尝试将事件侦听器添加到 React 中的图标,但它不起作用。我的代码:

const handleKey = (event) => {
    console.log(event);
}

<MdOutlinePause onKeyDown={handleKey} />

我在同一个按钮上还有一个 onClick 处理程序,它运行良好,所以我真的很困惑为什么 onKeyDown 没有触发。感谢任何人可以提供的任何帮助!

在特定元素上使用 onKeyDown 需要焦点才能按预期工作。

我们可以通过添加 tabindex 属性使您的 MdOutlinePause 组件 focus-able。这将允许您通过点击选项卡或单击它来为图标提供焦点。该元素看起来像这样:

<MdOutlinePause tabIndex={0} onKeyDown={handleKey} />

如果您想检测键事件而无需聚焦元素,您将需要附加一个事件侦听器。我们可以使用 useEffect 来做到这一点,所以这会发生在 Mount 上,我们会将事件侦听器附加到 window 所以无论哪个元素有焦点它都会工作:

  /// Don't copy and paste this - see below
  useEffect(() => {
    window.addEventListener("keydown", handleKeyPress);
    });
  }, []);
  /// Don't copy and paste this - see below

这让您可以检测键盘事件,这很好,但有一个问题:每当页面加载时 eventListener 都会再次添加,但永远不会被删除。因此,您可能会 运行 遇到函数在每次按键时被多次调用的问题。

我们可以通过删除 return from useEffect 中的事件监听器来解决这个问题。这就是我们如何指定一个函数让 useEffect 清理 自身。这确保我们的 useEffect 永远不会一次添加超过一个“keydown”事件侦听器:

  useEffect(() => {
    window.addEventListener("keydown", handleKeyPress);

    return () => {
      window.removeEventListener("keydown", handleKeyPress);
    };
  }, []);

这是完整的 example on codesandbox

如果您不需要在 handleKeyPress 函数中访问状态,这非常有用。


当用户执行某个操作时,需要对状态变量做一些事情是一个常见的用例,如果我们需要访问更新后的状态,那么我们 运行 就会遇到问题:我们附加了 handleKeyPress 到事件侦听器 onMount 并且该函数永远不会更新,因此它将始终使用状态的初始版本。 This codesandbox 说明了这个问题。

我们可以通过将 handleKeyPress 添加到 useEffect 的依赖项数组来解决这个问题,但这会导致我们的事件侦听器被删除并且每个渲染器上的 re-added 因为 handleKeyPress 将被更新在每个渲染器上。更好的解决方案是 callback 模式 与我们的 handleKeyPress 一起使用,这样它仅在实际需要时才更新(当它所依赖的状态被更新时)。我们还想将 handleKeyPress 添加到我们的 useEffect 依赖数组中,以便在 handleKeyPress 函数更改时创建一个新的事件侦听器:

  const handleKeyPress = useCallback((event) => {
    // do stuff with stateVariable and event
  }, [stateVariable]);

  useEffect(() => {
    window.addEventListener("keydown", handleKeyPress);

    return () => {
      window.removeEventListener("keydown", handleKeyPress);
    };
  }, [handleKeyPress]);

现在一切都适当更新了! Here's a full example 在 codesandbox 上。


还有一种解决方案可以让您以最简单的方式访问状态。缺点是您唯一可以访问的状态变量(具有更新的值)将是您正在更新的状态变量,因此此解决方案是视情况而定的。要访问其他状态变量,您必须使用上述解决方案。

来自 passing a function to useState, we can access the previous state as the first argument of the function. This allows for a much simpler approach, shown below and in this codesandbox.

  const [text, setText] = useState("");

  const handleKeyPress = event => {
    setText(previousText => `${previousText}${event.key}`);
  };

  useEffect(() => {
    window.addEventListener("keydown", handleKeyPress);

    return () => {
      window.removeEventListener("keydown", handleKeyPress);
    };
  }, []);