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

React通知铃铛组件点击切换功能异常求助

问题分析与解决方案

你的代码存在几个关键问题,导致下拉列表无法通过再次点击铃铛容器关闭:

  1. 事件监听重复绑定且未清理:useEffect没有依赖数组和清理函数,每次组件渲染都会重复绑定document的点击事件,引发逻辑冲突。
  2. ref指向范围过小:refOne仅绑定在铃铛图标上,点击容器内的徽章或下拉区域时,contains判断会失效。
  3. 事件逻辑冲突:handleClick中无论点击是否在容器内都设置notifBadgeIsShown为false,同时容器的onClick又要切换状态,两者叠加导致状态异常。
  4. 事件冒泡未阻止:容器的点击事件会冒泡到document,触发handleClick直接关闭下拉。

修复后的代码

import { useState, useEffect, useRef } from 'react';
import { Effect } from 'react-notification-badge';
import NotificationBadge from 'react-notification-badge/lib/components/NotificationBadge';
import logoIcon from '../images/logo.svg';
import notificationIcon from '../images/notification.svg';

const TopBar = () => {
  const [notifBadgeIsShown, setNotifBadgeShown] = useState(false);
  const notificationWrapperRef = useRef(null);

  useEffect(() => {
    const handleDocumentClick = (e) => {
      // 点击容器外时关闭下拉
      if (notificationWrapperRef.current && !notificationWrapperRef.current.contains(e.target)) {
        setNotifBadgeShown(false);
      }
    };

    document.addEventListener('click', handleDocumentClick);
    // 组件卸载时移除事件监听,避免内存泄漏
    return () => document.removeEventListener('click', handleDocumentClick);
  }, []); // 空依赖数组确保事件只绑定一次

  const toggleNotifications = (e) => {
    // 阻止事件冒泡,避免触发document的click事件
    e.stopPropagation();
    setNotifBadgeShown(prev => !prev);
  };

  const notifications = ['Notification 1', 'Notification 2'];
  const listItems = notifications.map((item) => (
    <li key={item}>{item}</li>
  ));

  return (
    <div className="top-bar">
      <img className="logo-icon" src={logoIcon} alt="Logo icon" />
      <div
        className="notification-wrapper"
        onClick={toggleNotifications}
        ref={notificationWrapperRef} // ref绑定到整个容器,覆盖所有可点击区域
      >
        {!notifBadgeIsShown && (
          <NotificationBadge
            className="notif-badge"
            count={notifications.length}
            effect={Effect.SCALE}
          />
        )}
        {notifBadgeIsShown && <div className="dropdown">{listItems}</div>}
        <img
          className="notification-icon"
          src={notificationIcon}
          alt="Notification icon"
        />
      </div>
    </div>
  );
};

export default TopBar;

关键修改点

  • 扩大ref范围:将ref绑定到整个notification-wrapper容器,确保能检测到容器内所有元素的点击操作。
  • 优化事件监听:给useEffect添加空依赖数组和清理函数,避免重复绑定事件,防止内存泄漏。
  • 分离事件逻辑:
    • 容器的onClick仅负责切换下拉状态,并通过e.stopPropagation()阻止事件冒泡到document。
    • document的点击事件仅处理容器外的点击,触发下拉关闭。
  • 简化状态切换:使用函数式更新setNotifBadgeShown(prev => !prev),让状态切换更简洁可靠。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 23:26:05