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

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

问题分析

  1. 异步操作错误:getDocs是异步函数,直接赋值得到的是Promise对象,无法直接通过DocumentSnapshot.length获取文档数量。
  2. 状态未绑定:普通变量q的修改不会触发组件重新渲染,useEffect依赖为空也不会响应查询变化。
  3. 分页逻辑错误:next函数仅修改q但未更新组件状态;prev函数中endBefore(first['time','desc'])写法无效,分页逻辑不成立。
  4. 按钮未绑定事件:「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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 07:55:19