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

React聊天应用:仅接收当前认证用户外的浏览器通知问题

问题分析与修复

你的问题核心在于通知逻辑的位置和判断条件错误:

  • 你把通知触发放在了自己发送消息的handleSubmit函数里,而且判断条件newMessage.user !== auth.currentUser.displayName完全不成立——newMessage是输入框的文本字符串,根本没有user属性,所以newMessage.user是undefined,和当前用户名对比永远为true,导致自己发消息时触发通知。
  • 其他用户发送消息时,不会执行你的handleSubmit函数,自然不会触发通知。

修复步骤

  1. 移除handleSubmit函数里的错误通知代码
  2. 将通知逻辑移到监听messages变化的useEffect中,和播放声音的逻辑复用同一个判断条件

修改后的完整代码

import { useEffect, useState, useRef } from "react"; // 新增useRef,解决变量名冲突问题
import { addDoc, collection, serverTimestamp, onSnapshot, query, where, orderBy } from "firebase/firestore";
import { auth, db } from "../firebase-config";
import "../styles/Chat.css";
import IDLogo from "../images/IDLogo.png";
import notificationSound from "../sounds/message_sound.mp3";
import addNotification from "react-push-notification";
import DefaultProfilePicture from "../images/default_pfp.jpeg";

export const Chat = props => {
  const { room } = props;
  const [newMessage, setNewMessage] = useState(""); // 初始化空字符串,避免undefined问题
  const [messages, setMessages] = useState([]);

  const messagesCollectionRef = collection(db, "messages"); // 重命名集合引用,避免和DOM引用冲突
  const messagesContainerRef = useRef(null); // 单独定义DOM容器引用

  useEffect(() => {
    const queryMessages = query(messagesCollectionRef, where("room", "==", room), orderBy("createdAt"));
    const unsubscribe = onSnapshot(queryMessages, snapshot => {
      let messages = [];
      snapshot.forEach(doc => {
        messages.push({ ...doc.data(), id: doc.id });
      });
      setMessages(messages);
    });

    return () => unsubscribe();
  }, [room]); // 依赖项加入room,确保切换房间时重新监听

  useEffect(() => {
    if (messagesContainerRef.current) {
      messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
    }
  }, [messages]); // 依赖项改为messages,确保消息更新时滚动到底部

  const handleSubmit = async e => {
    e.preventDefault();
    if (!newMessage.trim()) return; // 更严谨的空内容判断

    const currentUser = auth.currentUser;
    if (!currentUser) return; // 增加未登录状态容错

    await addDoc(messagesCollectionRef, {
      text: newMessage,
      createdAt: serverTimestamp(),
      user: currentUser.displayName,
      profilePicture: currentUser.photoURL,
      room,
    });

    setNewMessage("");
  };

  useEffect(() => {
    const audio = new Audio(notificationSound);
    const lastMessage = messages[messages.length - 1];

    if (lastMessage && lastMessage.user !== auth.currentUser?.displayName) { // 可选链避免未登录报错
      audio.play();
      // 在这里触发新消息通知
      addNotification({
        title: "New Message",
        message: lastMessage.text,
        duration: 5000,
        native: true,
        icon: IDLogo,
      });
    }
  }, [messages]);

  return (
    <div className='chat'>
      <div className='header'>
        <h1>
          Welcome to: <span className='Chat__room--title'>{room.toUpperCase()}</span>
        </h1>
      </div>

      <div ref={messagesContainerRef} className='messages'>
        {messages.map(message => (
          <p key={message.id} className='Chat__message'> {/* 增加key属性,符合React列表渲染规范 */}
            {message.profilePicture ? (
              <img className='profile__picture' referrerpolicy='no-referrer' src={message.profilePicture} alt={message.user} />
            ) : (
              <img className='profile__picture' src={DefaultProfilePicture} alt={message.user}></img>
            )}
            {message.user} : {message.text}
          </p>
        ))}
      </div>
      <div className='send__message__container'>
        <form onSubmit={handleSubmit} className='new__message__form'>
          <input
            spellCheck='false'
            onChange={e => setNewMessage(e.target.value)}
            className='new__message__input'
            placeholder='Type a message here...'
            value={newMessage}
          />
          <button type='submit' className='signOut--sendMessage__button'>
            Send
          </button>
        </form>
      </div>
    </div>
  );
};

额外优化说明

  • 解决变量名冲突:原代码中messagesRef同时用于Firestore集合和DOM元素引用,重命名后避免逻辑混淆。
  • 完善依赖项:监听Firestore的useEffect加入room依赖,确保切换房间时自动更新监听。
  • 增加容错处理:新增未登录状态判断、空内容判断,避免异常场景报错。
  • 规范React列表:给消息列表项增加key属性,符合React渲染要求。

内容的提问来源于stack exchange,提问作者João Silva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 12:37:51