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
相关产品推荐
相关产品推荐

