【问题标题】:TypeScript - Navigate dropdown listitems using keyboardTypeScript - 使用键盘导航下拉列表项
【发布时间】:2023-03-29 08:38:01
【问题描述】:

我正在开发一个开源项目,但遇到了一个错误。我无法使用键盘(箭头键/标签)导航下拉列表项。我已经编写了键盘导航逻辑,但不太确定如何实现它。下面是代码sn-p。

.
.
.

const TopNavPopoverItem: FC<ComponentProps> = ({closePopover, description, iconSize, iconType, title, to}) => {
  const history = useHistory();

  const handleButtonClick = (): void => {
    history.push(to);
    closePopover();
  };

  const useKeyPress = function (targetKey: any) { // where/how am I supposed to use this function?
    const [keyPressed, setKeyPressed] = useState(false);
    function downHandler(key: any) {
      if (key === targetKey) {
        setKeyPressed(true);
      }
    }
    const upHandler = (key: any) => {
      if (key === targetKey) {
        setKeyPressed(false);
      }
    };

    React.useEffect(() => {
      window.addEventListener('keydown', downHandler);
      window.addEventListener('keyup', upHandler);
      return () => {
        window.removeEventListener('keydown', downHandler);
        window.removeEventListener('keyup', upHandler);
      };
    });
    return keyPressed;
  };


  return (
    <button className="TopNavPopoverItem" onClick={handleButtonClick}>
      <Icon className="TopNavPopoverItem__icon" icon={iconType} size={iconSize} />
      <div className="TopNavPopoverItem__right">
        <span className="TopNavPopoverItem__title">{title}</span>
        <span className="TopNavPopoverItem__description">{description}</span>
      </div>
    </button>
  );
};

任何解决方法或修复? 提前致谢。

【问题讨论】:

标签: html reactjs typescript ecmascript-2016


【解决方案1】:

应始终在文件的顶层定义自定义挂钩。它不能在组件内部。组件使用钩子,但不拥有钩子。

你有一个钩子,它接受一个键名作为参数并返回一个boolean 指示当前是否正在按下该键。这是正确的想法,但它有一些错误。

当您开始添加更好的 TypeScript 类型时,您会发现事件侦听器的参数需要是 event——而不是键。您可以将密钥作为事件的属性来访问。

(注意:由于我们直接附加到窗口,因此事件是 DOM KeyboardEvent 而不是 React.KeyboardEvent 合成事件。)

你的useEffect 钩子应该有一些依赖关系,这样它就不会在每次渲染时都运行。这取决于targetKey。我在 CodeSandbox 中编写我的代码,在那里我收到有关“详尽依赖项”的警告,因此我还添加了 setKeyPressed 作为依赖项并将两个处理程序移动到 useEffect 中。

我看到您有一个处理程序为function,一个为const。仅供参考,在这种情况下你使用哪个并不重要。

我们修改后的钩子如下所示:

import { useState, useEffect } from "react";

export const useKeyPress = (targetKey: string) => {
  const [keyPressed, setKeyPressed] = useState(false);

  useEffect(
    () => {
      const downHandler = (event: KeyboardEvent) => {
        if (event.key === targetKey) {
          setKeyPressed(true);
        }
      };

      const upHandler = (event: KeyboardEvent) => {
        if (event.key === targetKey) {
          setKeyPressed(false);
        }
      };

      // attach the listeners to the window.
      window.addEventListener("keydown", downHandler);
      window.addEventListener("keyup", upHandler);

      // remove the listeners when the component is unmounted.
      return () => {
        window.removeEventListener("keydown", downHandler);
        window.removeEventListener("keyup", upHandler);
      };
    },
    // re-run the effect if the targetKey changes.
    [targetKey, setKeyPressed]
  );

  return keyPressed;
};

我不知道你打算使用这个钩子,但这是一个虚拟的例子。我们在按下空格键时在屏幕上显示一个红色框,否则显示一条消息。

确保调用钩子时使用的键名is the correct key name。对于空格键,它是" "

import { useKeyPress } from "./useKeyPress";

export default function App() {
  const isPressedSpace = useKeyPress(" ");

  return (
    <div>
      {isPressedSpace ? (
        <div style={{ background: "red", width: 200, height: 200 }} />
      ) : (
        <div>Press the Spacebar to show the box.</div>
      )}
    </div>
  );
}

CodeSandbox Link

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-12
    • 2023-03-13
    • 2011-05-15
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2014-09-29
    • 2017-06-10
    相关资源
    最近更新 更多