Firestore .startAt 无法正常工作

Posted

技术标签:

【中文标题】Firestore .startAt 无法正常工作【英文标题】:Firestore .startAt is not working properly 【发布时间】:2019-03-13 10:46:03 【问题描述】:
infiniteHandler($state) 
    var next = db
        .collection("posts")
        .orderBy("timestamp", "desc")
        .startAfter(this.lastVisible)
        .limit(3)

    next.get().then(documentSnapshots => 
        //Get the last visible document
        // this.lastVisible =
        // documentSnapshots.docs[documentSnapshots.docs.length - 1]

        if (documentSnapshots.docs.length == 0) $state.complete()
        else 
            this.$store.commit(
                "modules/posts/updateLastVisible",
                documentSnapshots.docs[documentSnapshots.docs.length - 1].data()
                .timestamp
            )
        

        documentSnapshots.forEach(doc => 
            var post = doc.data()
            post.docID = doc.id
            this.$store.commit("modules/posts/pushPost", post)
        )
        $state.loaded()
    )

这是我的无限加载处理程序,一旦到达列表末尾,它就会获取新的数据库条目。到目前为止工作正常。

这是我在页面加载时的第一次抓取

async fetch( store )
    if (store.state.modules.posts.posts.length < 5) 
        let posts = []
        await db
            .collection("posts")
            .orderBy("timestamp", "desc")
            .limit(3)
            .get()
            .then(querySnapshot => 
                store.commit(
                    "modules/posts/updateLastVisible",
                    querySnapshot.docs[2].data().timestamp
                )
                querySnapshot.forEach(doc => 
                    var x = doc.data()
                    x.docID = doc.id
                    posts.push(x)
                )
            )
        store.commit("modules/posts/fetchedPosts", posts)
    

基本上,问题是当我在无限加载处理程序中获取时,我在页面加载时再次获取前 3 个条目,这导致条目显示两次,这不应该发生,因为 this.lastVisible 有我在加载时获取的第三个元素的时间戳,所以应该忽略这些。

在这些元素之后,.startAfter 一切正常,但前 3 个再次加载没有意义。 我用 devtools 检查了商店,一切正常,this.lastVisible 在第一次调用无限加载处理程序时具有正确的值。

赏金编辑: 好的,所以我仍然有这个问题,我尝试更多地解决它以找到问题,但它仍然在发生......我现在将设置一个赏金,我希望任何人都能够提供帮助。

【问题讨论】:

我感觉这与在 this.lastVisible 设置之前运行的infiniteHandler() 有关。尝试在函数顶部记录 lastVisible。 @SeanRussell 抱歉,回复晚了,是的,它确实第一次记录了一个无效的对象,这导致它基本上从整个列表的入口点开始,但我怎么能改变这种行为? 在 infaniteHandler() 中进行初始调用,它似乎在您第一次异步 fetch() 后工作 @SeanRussell 但在提取完成之前无限处理程序不应该能够工作吗? 我的意思是调整您的逻辑/代码以在一个功能中执行操作。你说除了前三个文件被复制之外它还有效。好吧,废弃调用前三个文档的函数。 【参考方案1】:

您实际上并不需要第一次提取。挂载时将自行调用infiniteHandler。如果它没有调用,那么您可以尝试使用该函数

this.$refs.infiniteLoading.attemptLoad(); // 'infiniteLoading' 是组件的 ref 属性

这实际上会为你调用infiniteHandler函数。

编辑:检查其中一个功能是否正在运行。在处理程序部分

infiniteHandler($state) 
    //Check if its currently loading
    this.$nextTick(()=>
       if (this.isDocSnapShotLoading)
           return;
       
    );

    //set as currently loading
    this.isDocSnapShotLoading = true;

    var next = db
        .collection("posts")
        .orderBy("timestamp", "desc")
        .startAfter(this.lastVisible)
        .limit(3)

    next.get().then(documentSnapshots => 
        //Get the last visible document
        // this.lastVisible =
        // documentSnapshots.docs[documentSnapshots.docs.length - 1]

        if (documentSnapshots.docs.length == 0) $state.complete()
        else 
            this.$store.commit(
                "modules/posts/updateLastVisible",
                documentSnapshots.docs[documentSnapshots.docs.length - 1].data()
                .timestamp
            )
        

        documentSnapshots.forEach(doc => 
            var post = doc.data()
            post.docID = doc.id
            this.$store.commit("modules/posts/pushPost", post)
        )
        $state.loaded()

        //set completed loading
        this.isDocSnapShotLoading = false;
    )

在获取部分

async fetch( store )
    if (store.state.modules.posts.posts.length < 5) 

        //check if currently loading
        this.$nextTick(()=>
           if (this.isDocSnapShotLoading)
              return;
           
       );

        //set as currently loading
        this.isDocSnapShotLoading = true;

        let posts = []
        await db
            .collection("posts")
            .orderBy("timestamp", "desc")
            .limit(3)
            .get()
            .then(querySnapshot => 
                store.commit(
                    "modules/posts/updateLastVisible",
                    querySnapshot.docs[2].data().timestamp
                )
                querySnapshot.forEach(doc => 
                    var x = doc.data()
                    x.docID = doc.id
                    posts.push(x)
                )

                //set as completed loading.
                this.isDocSnapShotLoading = false;
            )
        store.commit("modules/posts/fetchedPosts", posts)
    

【讨论】:

但是如果我在infiniteHandler 中使用我的Front Articles 获取逻辑,这是否意味着只有在触发infiniteLoading 时才会获取内容?即使我尝试在页面加载时调用它,我仍然没有 fetch nuxtjs.org/api/pages-fetch 在 s-s-r 和速度方面给我带来的好处 infiniteHandler 应该在您向下滚动以获取更多内容时继续加载其他内容。因此,即使在安装应用程序时调用它,当您向下滚动并且有更多内容时,它也会继续调用。 我不确定 s-s-r 部分,因为您更了解您的应用程序结构和设计,因此我无法就此提供建议。如果 s-s-r 确实对您的应用程序有帮助,那么您可以做的是维护一个商店或组件变量,并在获取工作时将其设置为 true。在infiniteHandler中检查这个变量,如果它是真的,就阻止它加载。 那么 fetch() 正在工作,因为我在首页上的 Big Images 中显示 3 个最新帖子,在此之下我有一个无限的加载列表,所以如果我们谈论同样的事情,那么 fetch 本身工作得很好很好,商店也填充了我传递给startAfter() 的正确值,所以我很难通过值检查等来做一个简单的解决方法。 上面的帖子已经在上面进行了编辑以添加检查。如果函数在同一个组件中,您可以使用组件变量。否则,您可能需要将其添加为商店状态。【参考方案2】:

好的,所以我找到了一个暂时的解决方案,它现在可以工作但仍然不漂亮:

documentSnapshots.forEach(doc => 
          if (
            doc.id !== this.posts[0].docID &&
            doc.id !== this.posts[1].docID &&
            doc.id !== this.posts[2].docID
          ) 
            var post = doc.data()
            post.docID = doc.id
            this.$store.commit("modules/posts/pushPost", post)
          
        )

我也尝试通过不同的解决方案来提高效率,感谢您的帮助。

【讨论】:

【参考方案3】:

如果您想忽略infiniteHandler 中的前3 个帖子,您可以创建一个帖子数组来存储帖子ID 并检查帖子ID 是否已经加载。我知道这应该使用查询来解决,但作为临时解决方案,我希望它对你有用。

infiniteHandler($state) 
    var next = db
        .collection("posts")
        .orderBy("timestamp", "desc")
        .startAfter(this.lastVisible)
        .limit(3)

    next.get().then(documentSnapshots => 
        //Get the last visible document
        // this.lastVisible =
        // documentSnapshots.docs[documentSnapshots.docs.length - 1]

        if (documentSnapshots.docs.length == 0) $state.complete()
        else 
            this.$store.commit(
                "modules/posts/updateLastVisible",
                documentSnapshots.docs[documentSnapshots.docs.length - 1].data()
                .timestamp
            )
        

        documentSnapshots.forEach(doc => 
            var check = this.postIdArray.indexOf(doc.id);
            if(check == -1)
                var post = doc.data()
                post.docID = doc.id
                this.$store.commit("modules/posts/pushPost", post);
                this.postIdArray[] = doc.id;
            
        )
        $state.loaded()
    )




async fetch( store )
    this.postIdArray = [];
    if (store.state.modules.posts.posts.length < 5) 
        let posts = []
        await db
            .collection("posts")
            .orderBy("timestamp", "desc")
            .limit(3)
            .get()
            .then(querySnapshot => 
                store.commit(
                    "modules/posts/updateLastVisible",
                    querySnapshot.docs[2].data().timestamp
                )
                querySnapshot.forEach(doc => 
                    var x = doc.data()
                    x.docID = doc.id
                    this.postIdArray[] = doc.id;
                    posts.push(x)
                )
            )
        store.commit("modules/posts/fetchedPosts", posts)
    

【讨论】:

谢谢你的想法,但我想在没有类似的东西的情况下开始它,但如果没有其他帮助,那么我可能需要这样的东西...... 您是否检查过您的 this.lastVisible 是否正在更新并在其中获取正确的值? 是的,我通过无限加载器中的 console.log 和 vuex devtools 做到了这一点,我所看到的是,当我在无限加载中记录 next 变量时,它第一次看起来不同与其他时间相比调用,而将相同的值传递给它... 你确定infiniteHandler只在用户被读取到页面结束并且第一次调用完成后调用,根据我的理解,第一次发生的事情没有任何内容,所以直接infiniteHandler和第一次调用正在同时调用,因此第一次和问题解决后数据相同。 很遗憾我不在家,所以我现在无法测试,但我会在我回家时检查并再次向您发送消息

以上是关于Firestore .startAt 无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

即使没有数据更改,Firestore 缓存也无法正常工作

isEqual to operator 在 Flutter Firestore 中无法正常工作

Firebase firestore onSnapshot 在 useEffect 中无法正常工作

Node.js 云功能“get() 中的 firestore set() 如果不存在”无法正常工作?

无法在正在进行内容生成时调用 StartAt

Firebase Firestore:无法访问Firestore后端(仅限Android)