React聊天应用:仅接收当前认证用户外的浏览器通知问题
问题分析与修复
你的问题核心在于通知逻辑的位置和判断条件错误:
- 你把通知触发放在了自己发送消息的
handleSubmit函数里,而且判断条件newMessage.user !== auth.currentUser.displayName完全不成立——newMessage是输入框的文本字符串,根本没有user属性,所以newMessage.user是undefined,和当前用户名对比永远为true,导致自己发消息时触发通知。 - 其他用户发送消息时,不会执行你的
handleSubmit函数,自然不会触发通知。
修复步骤
- 移除
handleSubmit函数里的错误通知代码 - 将通知逻辑移到监听
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
相关产品推荐
相关产品推荐

