如何将getRoomInfo中docSnap.data()的值存入变量?useState触发无限循环
解决React中Firestore房间数据存储与渲染问题
问题核心
你遇到的无限循环是因为直接在组件渲染流程中调用状态更新函数,导致组件反复重渲染;普通变量会在组件重渲染时被重置,所以JSX无法持续访问到数据。下面是具体的修复方案:
步骤1:新增状态存储房间详情
添加一个roomDetails状态来保存每个房间的完整信息,用对象存储(键为房间ID,值为房间数据)方便后续在JSX中快速查找:
const [roomDetails, setRoomDetails] = useState({});
步骤2:用useEffect控制数据获取时机
使用useEffect限定数据获取的触发条件,避免无限循环:
- 第一个
useEffect:当currentUser变化时(用户登录/登出),调用getListOfRooms获取用户加入的房间列表。 - 第二个
useEffect:当listOfRooms更新时,批量获取每个房间的详情并更新状态。
步骤3:改造异步数据获取逻辑
修改getRoomInfo和getRoomInfoHelper,用Promise.all批量处理异步请求,避免多次触发状态更新:
const getRoomInfo = async (roomId) => { const docRef = doc(db, "Rooms", roomId); const docSnap = await getDoc(docRef); return docSnap.exists() ? { id: roomId, ...docSnap.data() } : null; }; const getRoomInfoHelper = async () => { if (listOfRooms.length === 0) return; // 批量获取所有房间详情 const roomDataPromises = listOfRooms.map(s => getRoomInfo(s.id)); const roomDataList = await Promise.all(roomDataPromises); // 过滤掉无效数据,转成对象格式 const newRoomDetails = roomDataList.reduce((acc, room) => { if (room) acc[room.id] = room; return acc; }, {}); setRoomDetails(newRoomDetails); };
完整修改后的代码
import { React, useEffect, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import "../index.css"; import "bootstrap/dist/css/bootstrap.css"; import Navbar1 from "./NavBar1"; import { db } from "../firebase"; import { auth } from "../firebase"; import { useAuthValue } from "../context.js"; import { onAuthStateChanged } from "firebase/auth"; import { collection, getDocs, doc, updateDoc, arrayUnion, getDoc, } from "firebase/firestore"; const JoinedRooms = () => { const { currentUser } = useAuthValue(); const [listOfRooms, setListOfRooms] = useState([]); const [roomDetails, setRoomDetails] = useState({}); // 新增状态存储房间详情 useEffect(() => { if (!currentUser) return; // 用户未登录时不执行 const getListOfRooms = async () => { const docRef = doc(db, "Users", currentUser.uid); const docSnap = await getDoc(docRef); if (docSnap.exists()) { setListOfRooms(docSnap.data().Chatrooms); } else { console.log("No such document!"); } }; getListOfRooms(); }, [currentUser]); // 依赖currentUser,用户状态变化时重新获取 useEffect(() => { const getRoomInfo = async (roomId) => { const docRef = doc(db, "Rooms", roomId); const docSnap = await getDoc(docRef); return docSnap.exists() ? { id: roomId, ...docSnap.data() } : null; }; const getRoomInfoHelper = async () => { if (listOfRooms.length === 0) return; const roomDataPromises = listOfRooms.map(s => getRoomInfo(s.id)); const roomDataList = await Promise.all(roomDataPromises); const newRoomDetails = roomDataList.reduce((acc, room) => { if (room) acc[room.id] = room; return acc; }, {}); setRoomDetails(newRoomDetails); }; getRoomInfoHelper(); }, [listOfRooms]); // 依赖listOfRooms,列表更新时重新获取房间详情 // 在JSX中可以这样访问房间数据 return ( <div> <Navbar1 /> <div className="container mt-4"> <h2>我加入的房间</h2> <div className="row"> {listOfRooms.map(room => { const detail = roomDetails[room.id]; if (!detail) return <div key={room.id}>加载中...</div>; return ( <div key={room.id} className="col-md-4 mb-3"> <div className="card"> <div className="card-body"> <h5 className="card-title">{detail.name}</h5> <p className="card-text">{detail.description}</p> {/* 其他房间信息 */} </div> </div> </div> ); })} </div> </div> </div> ); }; export default JoinedRooms;
关键说明
- 避免无限循环:所有数据获取逻辑都放在
useEffect中,通过依赖数组控制触发时机,只有当依赖项变化时才执行。 - 异步批量处理:用
Promise.all同时发起多个Firestore请求,提升效率,并且一次性更新状态,减少组件重渲染次数。 - JSX访问数据:通过
roomDetails[room.id]可以直接拿到对应房间的详情,同时处理加载状态,避免渲染空数据。
内容的提问来源于stack exchange,提问作者Rahul
相关产品推荐
相关产品推荐

