You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React子组件中按ESC键无法关闭卡片的问题求助

解决ESC键无法关闭非模态全屏卡片的问题

问题背景

需求为按下ESC键关闭全屏非模态卡片,避免表单数据丢失。当前卡片作为子组件已通过useEffect添加全局键盘监听,但ESC键无响应;点击CrossIcon按钮可正常关闭,怀疑与非Modal类组件的特性有关。

可能原因及修复方案

1. 函数引用不稳定导致监听频繁重建

当前handleCloseEditCard函数每次组件渲染都会重新创建,导致useEffect依赖变化,反复执行移除/添加监听操作,可能引发时序问题导致监听失效。

修复: 使用useCallback包裹关闭函数,稳定引用:

const handleCloseEditCard = useCallback((): void => {
  setIsDrawerOpen(false);
}, [setIsDrawerOpen]);

2. 全局事件被其他元素阻止冒泡

页面中若有表单输入框等元素捕获keydown事件并调用event.stopPropagation(),会导致window层面的监听不到事件。

修复: 开启事件捕获模式,确保监听优先触发:

window.addEventListener('keydown', handleKeyDown, true);

// 移除监听时需对应传入第三个参数
return () => {
  window.removeEventListener('keydown', handleKeyDown, true);
};

3. 卡片未聚焦导致事件优先级问题

非模态框默认不会自动获取焦点,浏览器可能优先处理其他聚焦元素的键盘事件。

修复: 给卡片根元素添加tabIndex,在卡片打开时主动聚焦:

// 添加ref引用卡片根元素
const cardRef = useRef<HTMLDivElement>(null);

// 卡片打开时自动聚焦
useEffect(() => {
  if (isDrawerOpen && cardRef.current) {
    cardRef.current.focus();
  }
}, [isDrawerOpen]);

// 给根Stack组件绑定ref和tabIndex
<Stack
  ref={cardRef}
  tabIndex={-1} // 允许聚焦但不参与tab导航
  // ...其他属性
>

4. 键值判断兼容性问题

部分旧版浏览器对Escape键的键值判断存在差异,可兼容处理:

const handleKeyDown = (event: KeyboardEvent) => {
  if (event.key === 'Escape' || event.key === 'Esc' || event.keyCode === 27) {
    handleCloseEditCard();
  }
};

完整修复代码示例

import { useCallback, useEffect, useRef } from 'react';
import { Stack, Typography, Chip } from '@mui/material';
import { CrossIcon, FashionForwardIcon } from './your-icons-path';
import { useStyleDialog } from './your-hooks-path';
import { useTheme } from '@mui/material/styles';

export const EditStyleCardHeader = (
  { isMoreSustainableChoice = false, isBi2Style, hasAllData }: EditStyleCardHeaderProps
): React.ReactElement => {
  const { setIsDrawerOpen, editStyleNumber, isDrawerOpen } = useStyleDialog();
  const { palette } = useTheme();
  const cardRef = useRef<HTMLDivElement>(null);

  const handleCloseEditCard = useCallback((): void => {
    setIsDrawerOpen(false);
  }, [setIsDrawerOpen]);

  useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === 'Escape' || event.key === 'Esc' || event.keyCode === 27) {
        handleCloseEditCard();
      }
    };

    window.addEventListener('keydown', handleKeyDown, true);

    return () => {
      window.removeEventListener('keydown', handleKeyDown, true);
    };
  }, [handleCloseEditCard]);

  useEffect(() => {
    if (isDrawerOpen && cardRef.current) {
      cardRef.current.focus();
    }
  }, [isDrawerOpen]);

  return (
    <Stack
      ref={cardRef}
      tabIndex={-1}
      direction="row"
      justifyContent="space-between"
      height={132}
      paddingLeft={9}
      paddingRight={10}
      minHeight={132}
      sx={{ borderBottom: `1px solid ${palette.divider}` }}
    >
      <Stack direction="row">
        <Stack direction='column' marginY={9}>
          <Stack direction='row' alignItems='center'>
            <CrossIcon
              style={{
                cursor: 'pointer',
                marginRight: 16,
              }}
              onClick={handleCloseEditCard}
            />
            <Typography variant="headings.h2">EDIT STYLE</Typography>
          </Stack>
          <Stack direction='row' marginTop={2} marginLeft={10}>
            <Typography
              marginRight={2}
              variant='body.default'
              sx={{ color: palette.text.secondary }}
            >
              {editStyleNumber}
            </Typography>
            {isMoreSustainableChoice && (
              <FashionForwardIcon
                color={palette.success.main}
              />
            )}
            {isBi2Style && hasAllData
              && <Chip size='small' label='BI2 Style' sx={{ marginLeft: 2 }} />}
          </Stack>
        </Stack>
      </Stack>
    </Stack>
  );
};

内容的提问来源于stack exchange,提问作者Jdeveloper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 14:22:18