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

React中使用useEffect时按ESC键同时关闭两个弹窗如何解决

弹窗ESC键同时关闭多个弹窗解决方案

问题根因

你当前的实现中,每个弹窗打开时都会直接在全局window对象上绑定ESC按键监听事件,多个弹窗同时开启时,所有绑定的回调函数会被同时触发,因此会出现按一次ESC关闭所有弹窗的问题。

解决方案

方案1:全局弹窗栈管理(推荐,适配多弹窗复杂场景)

全局维护一个弹窗打开队列,仅允许最后打开的最顶层弹窗响应ESC关闭操作:

  1. 首先定义全局弹窗栈:
// 全局存储打开的弹窗关闭方法,栈尾为当前最顶层弹窗
const popupStack: Array<() => void> = []
  1. 修改弹窗的事件绑定逻辑:
useEffect(() => {
  let escHandler: ((event: KeyboardEvent) => void) | null = null
  if(isOpen && closePopupByClickingOutside) {
    // 弹窗开启时将当前关闭方法推入栈顶
    popupStack.push(closePopup)
    escHandler = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        // 仅调用栈顶的弹窗关闭方法
        const topCloseFn = popupStack.pop()
        topCloseFn?.()
        // 阻止事件触发其他绑定的监听回调
        event.stopImmediatePropagation()
      }
    }
    window.addEventListener('keydown', escHandler)
  }

  return () => {
    if(escHandler) {
      window.removeEventListener('keydown', escHandler)
      // 弹窗卸载时从栈中移除对应的关闭方法
      const currentIndex = popupStack.indexOf(closePopup)
      if(currentIndex > -1) popupStack.splice(currentIndex, 1)
    }
  }
// 补充完整依赖项,避免闭包问题
}, [isOpen, closePopup, closePopupByClickingOutside])

方案2:组件内独立绑定事件(适合轻量场景)

将按键事件绑定到弹窗自身DOM节点而非全局window,仅让当前聚焦的弹窗响应ESC:

  1. 给弹窗根节点设置可聚焦属性并绑定按键事件:
const popupRef = useRef<HTMLDivElement>(null)

return (
  <div 
    className="popup-root"
    ref={popupRef}
    // 设置tabIndex让div可接收键盘事件
    tabIndex={-1}
    onKeyDown={(e) => {
      if(e.key === 'Escape') closePopup()
    }}
  >
    <div className="close-popup" onClick={() => closePopup()}></div>
    {/* 其他弹窗内容 */}
  </div>
)
  1. 弹窗打开时自动聚焦到自身节点:
useEffect(() => {
  if(isOpen) {
    popupRef.current?.focus()
  }
}, [isOpen])

注意事项

原useEffect缺少closePopup、closePopupByClickingOutside依赖项,修改时请补充完整,避免出现闭包导致的逻辑异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 10:39:02