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

React+Firebase Firestore中如何条件渲染加载更多按钮

React + Firestore 加载更多按钮条件渲染修复方案

原有代码问题

  • 按钮显示逻辑错误:使用documents.length > 2作为判断条件,首次加载2条数据时条件不成立,哪怕集合内有上百条数据也不会显示按钮;且数据全部加载完成后,只要已加载条数大于2按钮会一直显示,不符合需求。
  • 自定义Hook缺少必要状态:没有记录集合总文档数、没有判断是否还有剩余数据可加载的标识,且updateState方法存在闭包陷阱,多次触发加载更多时可能出现数据覆盖/丢失问题。

修复代码

1. 改造useCollection自定义Hook

首先新增总文档数、是否有更多数据两个状态,初始化时查询匹配条件的总文档数,加载更多时根据返回数据量判断是否已到末尾,同时修复闭包问题。

import { useEffect, useRef, useState } from "react"
// 新增引入count聚合查询方法
import {
  collection,
  getDocs,
  getCountFromServer,
  limit,
  onSnapshot,
  orderBy,
  query,
  startAfter,
  where,
} from "firebase/firestore"

import { db } from "../firebase/config"

export const useCollection = (c, _q, _l, _o) => {
  const [documents, setDocuments] = useState([])
  const [isLoading, setIsLoading] = useState(true)
  const [error, setError] = useState(null)
  // 新增两个状态:总文档数、是否有更多可加载数据
  const [totalDocs, setTotalDocs] = useState(0)
  const [hasMore, setHasMore] = useState(false)

  const q = useRef(_q).current
  const o = useRef(_o).current

  useEffect(() => {
    let ref = collection(db, c)
    if (q) {
      ref = query(ref, where(...q))
    }
    if (o) {
      ref = query(ref, orderBy(...o))
    }
    // 先查询匹配条件的总文档数
    const getTotal = async () => {
      const countSnapshot = await getCountFromServer(ref)
      const total = countSnapshot.data().count
      setTotalDocs(total)
      // 总条数大于每页加载数时,才显示加载更多按钮
      setHasMore(total > _l)
    }
    getTotal()

    // 分页查询逻辑
    const pageQuery = _l ? query(ref, limit(_l)) : ref
    const unsubscribe = onSnapshot(pageQuery, (snapshot) => {
      const results = []
      snapshot.docs.forEach(
        (doc) => {
          results.push({ ...doc.data(), id: doc.id })
        },
        (error) => {
          console.log(error)
          setError("could not fetch the data")
        }
      )
      setDocuments(results)
      setIsLoading(false)
      setError(null)
    })

    return unsubscribe
  }, [c, q, o, _l])

  const fetchMore = async (doc) => {
    setIsLoading(true)
    const q = query(
      collection(db, c),
      orderBy(...o),
      ...(q ? [where(...q)] : []),
      startAfter(doc.createdAt),
      limit(_l)
    )
    const newDocuments = await getDocs(q)
    updateState(newDocuments)
    setIsLoading(false)
  }

  const updateState = (docs) => {
    if (!docs.empty) {
      const tempPosts = []
      docs.forEach((document) => {
        tempPosts.push({
          id: document.id,
          ...document.data(),
        })
      })
      // 用函数式更新修复闭包问题,避免拿到旧的documents值
      setDocuments(prevDocs => [...prevDocs, ...tempPosts])
      // 如果本次返回的文档数小于每页限制数,说明已经加载完所有数据,隐藏按钮
      if (tempPosts.length < _l) {
        setHasMore(false)
      }
    } else {
      // 没有返回新文档,直接标记无更多数据
      setHasMore(false)
    }
  }

  // 新增返回hasMore状态
  return { documents, fetchMore, error, isLoading, hasMore, totalDocs }
}

改动点说明:

  • 引入getCountFromServer查询符合条件的总文档数,初始化时判断总条数是否大于单页加载数,决定初始是否显示按钮
  • 新增hasMore状态统一控制按钮显示
  • 修复updateState的闭包陷阱,改用函数式更新状态
  • 每次加载更多后判断返回数据量,不足单页数量时标记为无更多数据,自动隐藏按钮
  • 补全fetchMore中的where条件,避免带过滤条件的查询加载更多时结果错误

2. 修改SolutionComments组件的按钮渲染逻辑

直接用hasMore作为按钮显示的判断条件即可,同时给加载状态的按钮加禁用属性,避免重复点击。

import React, { useState } from "react"
import { useParams } from "react-router-dom"

import { useCollection } from "../../hooks/useCollection"

import Comment from "./Comment"
import CommentForm from "./CommentForm"

const SolutionComments = () => {
  const [activeComment, setActiveComment] = useState(null)
  const { id } = useParams()
  // 从hook中解构hasMore状态
  const { documents, fetchMore, isLoading, hasMore } = useCollection(
    `solutions/${id}/comments`,
    null,
    2,
    ["createdAt", "desc"]
  )

  const fetchMoreComments = () => {
    // 加载中禁止重复触发
    if (isLoading) return
    fetchMore(documents[documents.length - 1])
  }

  return (
    <div className="mt-10">
      <CommentForm docID={id} />
      <div>
        {documents &&
          documents.map((comment) => (
            <Comment
              key={comment.id}
              comment={comment}
              replies={comment.replies}
              activeComment={activeComment}
              setActiveComment={setActiveComment}
            />
          ))}
      </div>
      {/* 用hasMore替换原来的错误判断条件 */}
      {hasMore && (
        <button 
          onClick={fetchMoreComments} 
          className="text-white bg-purple-500"
          disabled={isLoading}
        >
          {!isLoading ? "Load More Comments!" : "Loading..."}
        </button>
      )}
    </div>
  )
}

export default SolutionComments

逻辑验证

  • 集合总文档数≤2时:初始化查询总条数后hasMore为false,按钮直接隐藏
  • 集合总文档数>2时:初始化后hasMore为true,按钮显示
  • 每次点击加载更多后:如果返回的新评论数小于2,说明已经到最后一页,hasMore设为false,按钮自动隐藏

内容的提问来源于stack exchange,提问作者r121

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 19:15:34