React+Firebase数组仅在代码保存时渲染问题求助
问题修复方案
问题根源
- 异步循环处理错误:
onSnapshot回调中使用forEach结合async/await,但forEach不支持异步函数的等待机制,导致所有getContactInfo异步请求还未完成,就执行了setLatestsChats(tempLatestChats),此时数组为空或不完整。 - 状态更新未被监听:
LatestChat组件中第二个useEffect的依赖数组为空,仅在组件挂载时执行一次,不会响应latestsChats的后续更新。 - 未清理Firebase订阅:
onSnapshot的订阅未被清理,可能导致内存泄漏和重复监听。
修复步骤
1. 修复useChatFunctions中的异步逻辑与订阅清理
将forEach替换为map,通过Promise.all等待所有联系人信息请求完成后再更新状态,同时返回订阅清理函数:
export const useChatFunctions = () => { const [messages, setMessages] = useState([]); const [chatId, setChatId] = useState(""); const [loadingMessage, setLoadingMessage] = useState(undefined); const [contactName, setContactName] = useState(""); const [latestsChats, setLatestsChats] = useState([]); const fetchLatestChats = (userId) => { const q = query( collection(db, "chat"), where("arrayUsersId", "array-contains", userId) ); const unsubscribe = onSnapshot(q, async (querySnapShot) => { const tempLatestChats = await Promise.all( querySnapShot.docs.map(async (doc) => { const chatData = doc.data(); const otherUserId = chatData.arrayUsersId.find(id => id !== userId); const contactInfo = await getContactInfo(otherUserId); return { ...chatData, contactInfo, otherUserId }; }) ); setLatestsChats(tempLatestChats); }); return unsubscribe; }; const getContactInfo = async (userId) => { const q = query(collection(db, "user"), where("userId", "==", userId)); const querySnapshot = await getDocs(q); if (!querySnapshot.empty) { const userDoc = querySnapshot.docs[0]; return userDoc.data(); } else { return { displayName: "Nome do Contato Desconhecido", profilePictureUrl: "" }; } }; return { fetchChat, fetchLatestChats, sendMessage, messages, chatId, contactName, latestsChats, }; };
2. 修复LatestChat组件的状态监听
移除多余的latestsChatsList状态,直接使用latestsChats,同时在useEffect中清理Firebase订阅:
import { useEffect } from "react"; import { useChatFunctions } from "../../hooks/useChatFunctions"; import styles from "./LatestChat.module.css"; const LatestChat = ({ userId, setContactId }) => { const { fetchLatestChats, latestsChats } = useChatFunctions(); useEffect(() => { const unsubscribe = fetchLatestChats(userId); return () => unsubscribe(); }, [userId, fetchLatestChats]); return ( <ul className={styles.chats_list}> {latestsChats.map((chat, i) => ( <li key={chat.chatId || i} onClick={() => setContactId(chat.otherUserId)}> <div className={styles.profilePicture}> <img src={chat.contactInfo.profilePictureUrl || "/default-avatar.png"} alt={chat.contactInfo.displayName} /> </div> <div className={styles.messages_info}> <p className={styles.contact_name}> {chat.contactInfo.displayName} </p> <p className={styles.last_message}> {chat.messages?.[chat.messages.length - 1]?.message || "无消息"} </p> </div> </li> ))} </ul> ); }; export default LatestChat;
额外优化点
- 优先使用聊天文档的唯一
chatId作为列表项key,比索引更稳定。 - 添加可选链
?.和默认值,避免数据未加载时的渲染报错。 getContactInfo返回统一格式的对象,避免联系人不存在时的属性访问错误。
内容的提问来源于stack exchange,提问作者Alcides Gabriel
相关产品推荐
相关产品推荐

