在 React 应用程序中,我有一个由
<Card />
组件组成的网格。每个 <Card />
渲染多个 <Button />
组件。
由于我无法控制的限制,
Button
是一个低效的组件。渲染成本很高,而且我们需要渲染数百个<Card />
组件,因此它成为性能问题。
考虑到这一点,我只想在用户主动与
<Button />
交互时安装/渲染
<Card />
:
<Card />
上时,安装 <Button />
<Card />
时,安装并聚焦 <Button />
。<Card />
组件之间完全向前/向后循环悬停部分相当简单,我的实现如下所示。
我的键盘部分遇到问题。由于
<Button />
尚未安装,用户键盘如何导航到它?
这大部分都有效,除了两个重要的方式:
<Button />
被插入时,第一个 <Card/>
应该立即获得焦点(不需要按第二次 Tab 键)<Button>
获得焦点。它应该聚焦于“最后一个”按钮,并允许用户导航所有按钮,然后转到上一个 <Card>
问题:我必须对下面的代码进行哪些修改才能支持以上两点?
import { useState, Fragment, useEffect, useRef, MutableRefObject } from "react";
import Button from "./Button";
export default function Card() {
const [isHovered, setIsHovered] = useState<boolean>(false);
const [showButtons, setShowButtons] = useState<boolean>(false);
const containerRef: MutableRefObject<HTMLDivElement | null> = useRef(null);
useEffect(() => {
setShowButtons(isHovered);
}, [isHovered]);
return (
<div
onMouseOver={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
onFocus={(event) => {
event.persist();
setIsHovered(true);
}}
onBlur={(event) => {
event.persist();
window.requestAnimationFrame(() => {
if (containerRef.current) {
setIsHovered(
containerRef.current.contains(document.activeElement) === true
);
}
});
}}
tabIndex={0}
>
<div ref={containerRef}>
<h2>Card</h2>
{showButtons && (
<Fragment>
<Button>Expensive Button One</Button>
<Button>Expensive Button Two</Button>
</Fragment>
)}
</div>
</div>
);
}
您可以使用简单的 CSS(
opacity
属性)和焦点状态更改来达到所需的结果。
请注意,使非交互式卡片容器组件与
tabIndex={0}
交互是一种反模式。
//styles.css
.invisible-button {
opacity:0;
}
//Button.tsx
import React, { useState, FC } from "react";
export const Button: FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [hasBeenFocused, setHasBeenFocused] = useState(false);
const handleFocus = () => setHasBeenFocused(true);
const handleBlur = () => setHasBeenFocused(false);
return (
<button
className={hasBeenFocused ? "" : "invisible-button"}
onFocus={handleFocus}
onBlur={handleBlur}
onMouseEnter={handleFocus}
onMouseLeave={handleBlur}
>
{children}
</button>
);
};
export default Button;
看看完整的沙箱: https://codesandbox.io/p/sandbox/intelligent-matsumoto-frc9kk?file=%2Fsrc%2FApp.js%3A14%2C1
如果您希望仅让按钮获得焦点,则无需使卡片组件与 tabIndex 属性交互。
我希望我的解决方案有帮助:-)