Firebase v9结合React实现分页功能异常问题求助
问题描述
尝试为Firestore集合数据实现分页展示页面,但功能无法正常运行,获取文档列表长度的操作也失败。预期点击「Next」按钮显示下一组数据,点击「Previous」按钮显示上一组数据。
原代码
function Sessions() { const location= useLocation(); const user= location.state.user; const [sesList, setSesList] = useState([]); const subRef=collection(db, 'Users',user,'Sessions'); let q = query(subRef,orderBy('time','desc'),limit(5)); const DocumentSnapshot=getDocs(q); const last= DocumentSnapshot[DocumentSnapshot.length-1] const next=()=>{ return q=query(subRef,orderBy('time','desc'), startAfter(last), limit(5)) } const prev=(first)=>{ return q=query(subRef, orderBy('time','desc'), endBefore(first['time','desc']), limitToLast(5)) } useEffect(() => { //loads all the tenants onSnapshot(q, (snapshot) => { setSesList( snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id })) ); }); }, []); return ( <div className='Sessions'> <Sidebar className='nav' L1={<Link to='/Dashboard' state={{user:user}}>Dashboard</Link>} L2={<Link to='/Sessions' state={{user:user}}>Sessions</Link>}/> <div className="bod"> <div className="startCard1"> <h1>Sessions</h1> </div> <div className="Recent"> <h1>Your Sessions:</h1> <div className="recSes"> {sesList.map((REC)=>{ return( <div className="rowCard"> <div className="rowCardTitle"> <h3> Subject: {REC.subject}</h3> </div> <div className="WTime"> <h3>Work Minutes: {REC.WorkTime}</h3> </div> <div className="BTime"> <h3> Break Minutes: {REC.BreakTime}</h3> </div> <div className="Atime"> <h3>Date: {REC.time}</h3> </div> </div> ) })} </div> </div> <div className="buttoninlin"> <button className='Gobtn1'>Previous </button> <button className='Gobtn1' onClick={next}>Next</button> </div> </div> </div> ) } export default Sessions
问题分析
- 异步操作错误:
getDocs是异步函数,直接赋值得到的是Promise对象,无法直接通过DocumentSnapshot.length获取文档数量。 - 状态未绑定:普通变量
q的修改不会触发组件重新渲染,useEffect依赖为空也不会响应查询变化。 - 分页逻辑错误:
next函数仅修改q但未更新组件状态;prev函数中endBefore(first['time','desc'])写法无效,分页逻辑不成立。 - 按钮未绑定事件:「Previous」按钮没有绑定点击处理函数,无法触发上一页逻辑。
修正后的代码
import { useLocation, Link } from 'react-router-dom'; import { useState, useEffect } from 'react'; import { collection, query, orderBy, limit, startAfter, endBefore, limitToLast, onSnapshot } from 'firebase/firestore'; import { db } from './firebaseConfig'; // 替换为你的Firebase配置路径 function Sessions() { const location = useLocation(); const user = location.state.user; const [sesList, setSesList] = useState([]); const [currentQuery, setCurrentQuery] = useState(null); const [firstDoc, setFirstDoc] = useState(null); const [lastDoc, setLastDoc] = useState(null); const subRef = collection(db, 'Users', user, 'Sessions'); // 初始化查询 useEffect(() => { const initialQuery = query(subRef, orderBy('time', 'desc'), limit(5)); setCurrentQuery(initialQuery); }, [user]); // 根据当前查询监听数据 useEffect(() => { if (!currentQuery) return; const unsubscribe = onSnapshot(currentQuery, (snapshot) => { const docs = snapshot.docs.map(doc => ({ ...doc.data(), id: doc.id })); setSesList(docs); // 记录当前页的首尾文档快照 setFirstDoc(snapshot.docs[0] || null); setLastDoc(snapshot.docs[snapshot.docs.length - 1] || null); }); return unsubscribe; }, [currentQuery]); // 下一页处理 const handleNext = () => { if (!lastDoc) return; const nextQuery = query(subRef, orderBy('time', 'desc'), startAfter(lastDoc), limit(5)); setCurrentQuery(nextQuery); }; // 上一页处理 const handlePrev = () => { if (!firstDoc) return; const prevQuery = query(subRef, orderBy('time', 'desc'), endBefore(firstDoc), limitToLast(5)); setCurrentQuery(prevQuery); }; return ( <div className='Sessions'> <Sidebar className='nav' L1={<Link to='/Dashboard' state={{ user }}>Dashboard</Link>} L2={<Link to='/Sessions' state={{ user }}>Sessions</Link>} /> <div className="bod"> <div className="startCard1"> <h1>Sessions</h1> </div> <div className="Recent"> <h1>Your Sessions:</h1> <div className="recSes"> {sesList.map((REC) => ( <div className="rowCard" key={REC.id}> <div className="rowCardTitle"> <h3> Subject: {REC.subject}</h3> </div> <div className="WTime"> <h3>Work Minutes: {REC.WorkTime}</h3> </div> <div className="BTime"> <h3> Break Minutes: {REC.BreakTime}</h3> </div> <div className="Atime"> <h3>Date: {REC.time}</h3> </div> </div> ))} </div> </div> <div className="buttoninlin"> <button className='Gobtn1' onClick={handlePrev} disabled={!firstDoc}>Previous </button> <button className='Gobtn1' onClick={handleNext} disabled={!lastDoc}>Next</button> </div> </div> </div> ) } export default Sessions
修正说明
- 用
currentQuery状态绑定当前查询,修改时触发组件重新渲染和数据监听。 - 保存当前页的
firstDoc和lastDoc作为分页锚点,避免异步操作错误。 - 为按钮绑定处理函数,添加
disabled状态防止无效点击。 - 修复
map函数缺少唯一key的问题,符合React渲染规范。
内容的提问来源于stack exchange,提问作者Yash Ghela
相关产品推荐
相关产品推荐

