React中的onKeyDown/onKeyUp监听器

5

我正在尝试为React中的一个图标添加事件监听器,但是它没有起作用。我的代码:

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

<MdOutlinePause onKeyDown={handleKey} />

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


2
你是否正在聚焦图标,例如通过按Tab键直到它被突出显示?键事件会在聚焦的元素上触发。 - Nicholas Tower
1
尝试确保周围的元素具有键盘焦点。设置tabIndex应该可以实现这一点(https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex) - user2740650
1
我没有这样做,所以感谢你的提醒。我该如何让它在不需要聚焦元素的情况下仍然监听键盘事件? - Steven Talafous
不确定,但我会检查 https://www.w3schools.com/jsref/met_html_focus.asp(代码应在组件加载时运行)。 - user2740650
1个回答

6

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

我们可以通过添加tabindex属性使您的MdOutlinePause组件具有焦点功能。这将允许您通过按Tab或单击来使图标获得焦点。该元素可能如下所示:

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

如果您想检测关键事件,而无需将焦点集中在元素上,则需要附加事件侦听器。我们可以使用 useEffect 来实现这一点,以便在挂载时发生,并将事件侦听器附加到 window 上,以便它可以在任何有焦点的元素上工作。
  /// Don't copy and paste this - see below
  useEffect(() => {
    window.addEventListener("keydown", handleKeyPress);
    });
  }, []);
  /// Don't copy and paste this - see below

这样可以检测键盘事件,非常好,但有一个问题:每当页面加载时,事件侦听器都会被添加一次,但从未被移除。因此,您可能会遇到在每次按键时多次调用函数的问题。
我们可以通过在 useEffect返回的函数 中移除事件侦听器来解决这个问题。这是我们指定一个函数让 useEffect 在自身清理后运行的方式。这确保了我们的useEffect不会同时添加超过一个“keydown”事件侦听器:
  useEffect(() => {
    window.addEventListener("keydown", handleKeyPress);

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

这里有一个完整的 codesandbox示例

如果您的handleKeyPress函数中不需要访问状态,则此方法非常有效。


当用户进行某些操作时,需要对状态变量执行某些操作是一种常见的用例。如果我们需要访问更新后的状态,则会遇到问题:我们将handleKeyPress附加到onMount事件侦听器上,但该函数永远不会得到更新,因此它始终使用状态的初始版本。 这个codesandbox说明了这个问题。
我们可以通过将handleKeyPress添加到useEffect的依赖项数组中来解决这个问题,但这会导致我们的事件侦听器在每次渲染时被删除和重新添加,因为handleKeyPress将在每次渲染时更新。更好的解决方案是使用回调模式与我们的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]);


现在一切都更新得很恰当!这里是codesandbox上的完整示例

还有一种解决方案可以让您以最简单的方式访问状态。缺点是您只能访问到正在更新的状态变量(带有更新后的值),因此这种解决方案是情境性的。要访问其他状态变量,您必须使用上面介绍的解决方案。

通过将函数传递给useState,我们可以将先前的状态作为函数的第一个参数进行访问。这样可以实现更简单的方法,如下所示,并在此codesandbox中显示。

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

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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接