【问题标题】:JS Firestore 'load more' pagination ignoring startAfter() and loading old dataJS Firestore“加载更多”分页忽略 startAfter() 并加载旧数据
【发布时间】:2020-11-28 03:25:23
【问题描述】:

我正在实现一个分页,用户单击一个按钮来加载更多项目。 我正在关注https://firebase.google.com/docs/firestore/query-data/query-cursors 的文档(对查询进行分页)

我将最后一个项目 id 传递给 startAfter() 函数。这应该使下一个查询返回传入项 id 之后的项。

实际发生的是,之前的项目再次加载。 因此,如果初始提取看起来像:

[
 0: {id: "1", stuff: ...}
 1: {id: "2", stuff: ...}
 2: {id: "3", stuff: ...}
]

id 3 将被传递给 startAfter(),并且应该加载

[
 0: {id: "4", stuff: ...}
 1: {id: "5", stuff: ...}
]

相反,它会加载初始项目

[
 0: {id: "1", stuff: ...}
 1: {id: "2", stuff: ...}
]

所以当我按下“加载更多”按钮时,我最终会显示重复的项目。

初始获取函数:

  const [recipes, setRecipes] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const obj = [];
      const firstFetch = await db
        .collection('recipes')
        .orderBy('createdAt', 'desc')
        .limit(3)
        .get();

      const snapshot = await firstFetch;
      snapshot.forEach((doc) => {
        obj.push({ id: doc.id, ...doc.data() });
      });

      const allRecipes = {
        userRecipes: obj,
      };
      setRecipes(allRecipes.userRecipes);
    };
    fetchData();
  }, []);

配方变量日志:

[
 0: {id: "1", stuff: ...}
 1: {id: "2", stuff: ...}
 2: {id: "3", stuff: ...}
]

加载更多项目的按钮:

<button
  type="button"
  onClick={() => showMore({ recipe: recipes[recipes.length - 1] })}
>
  Show More
</button>

调用showMore函数:

  const showMore = async ({ recipe }) => {
    const data = await db
      .collection('recipes')
      .orderBy('createdAt', 'desc')
      .startAfter(recipe)
      .limit(2)
      .get();

    const moreData = data.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
    setRecipes([...recipes, ...moreData]);
  };

如果我记录 'moreData' 变量:

[
 0: {id: "1", stuff: ...}
 1: {id: "2", stuff: ...}
]

如果我记录更新后的“配方”变量:

[
 0: {id: "1", stuff: ...}
 1: {id: "2", stuff: ...}
 2: {id: "3", stuff: ...}
 0: {id: "1", stuff: ...}
 1: {id: "2", stuff: ...}
]

所以对我来说,它似乎忽略了 startAfter() 函数,而是从头开始加载所有内容。

任何关于我做错了什么的指针?

编辑: db 中的实际数据如下所示:

{
about: "Non magnam quasi tempore."
createdAt: t {seconds: 1596741068, nanoseconds: 531000000}
description: "Ea quibusdam odit quasi corporis et nostrum."
imageUrl: "http://lorempixel.com/640/480/food"
ingredients: [{…}]
likes: []
title: "Ella"
userId: "U8n09jb0ZENwrZhDeYrJUSikcM12"
userSaves: ["uM7A4r5O6ENiX3qTL4tGhDXYA3Z2"]
username: "foobar"
}

ID 是文档的名称。 ID 会在初始提取函数中附加到文档中。

解决方案

查看 Frank van Puffelen 的回答,我必须提供完整的快照(其中包括有关项目的所有信息),而不仅仅是包含项目的数组。

通过使用 useState 创建一个新的钩子,我可以在其中存储最后一个快照的状态:

const last = snapshot.docs[snapshot.docs.length - 1]
setLastDocSnap(last)

在我的 fetchMore() 函数中,我能够在 startAfter() 函数中设置状态:

const data = await db
  .collection('recipes')
  .orderBy('createdAt')
  .limit(limit)
  .startAfter(lastDocSnap)
  .get();

【问题讨论】:

    标签: javascript reactjs firebase google-cloud-firestore pagination


    【解决方案1】:

    我无法重现该问题。我已将您的代码简化为:

    ref.orderBy('id')
      .limit(3)
      .get()
      .then(function(querySnapshot) {
        console.log("Initial query: "+querySnapshot.docs.length+" results");
        querySnapshot.forEach(function(doc) {
          console.log(doc.data().id);
        })
        var last = querySnapshot.docs[querySnapshot.docs.length - 1];
        ref.orderBy('id')
          .startAfter(last)
          .limit(3)
          .get()
          .then(function(querySnapshot) {
            console.log("Second query: "+querySnapshot.docs.length+" results");
            querySnapshot.forEach(function(doc) {
              console.log(doc.data().id);
            })
          });
        });
    

    然后在包含 5 个文档的集合上运行它,id15。它的输出是:

    初始查询:3 个结果

    1

    2

    3

    第二次查询:2 个结果

    4

    5

    正如我所期望的那样。

    你可以在这里看到运行代码:https://jsbin.com/qusijaw/edit?js,console

    如果我的结果不能帮助您最终解决问题,您能否尝试在 jsbin 之类的环境中重现该问题,以便我们查看一下?

    【讨论】:

    • 将 snapshot.docs 和 snapshot.doc.data() 传递给 startAfter() 有区别吗? firestore 是否能够通过传入 doc.data 来确定从何处加载下一个项目?否则我可能不得不做一个大的重写。
    • Firestore 至少需要 createdAt 值和要启动的文档的文档 ID。由于您没有传递文档 ID,因此它没有足够的信息继续下去。传递DocumentSnapshot 是确保数据库拥有所需内容的最简单方法。
    • 谢谢。我相信就是这样。由于这是 Reactjs 相关的,我创建了一个名为 lastDocSnap 的新钩子,我将最后一个快照放入其中。 setLastDocSnap(snapshot.docs[snapshot.docs.length - 1])。我也在我的 fetchMore() 函数中传递了相同的内容。通过在“createdAt”之后排序,它可以很好地工作。由于我的文档中没有 id 字段(文档的名称是 id),我可能需要在创建时再写一次,以便将文档 id 也放入文档中。我想如果同时创建两个项目,仅对 createdAt 进行排序可能会很危险。但它似乎适用于 createdAt。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-21
    • 2016-01-09
    • 1970-01-01
    • 2016-09-26
    • 1970-01-01
    • 1970-01-01
    • 2016-05-02
    相关资源
    最近更新 更多