Using vuex to paginate through firestore data and seamlessly update the state with new information

After taking advice from Tony O'Hagan on Stack Overflow, I integrated the following code to exhibit a paginated query:

bindUsers: firestoreAction(({ bindFirestoreRef }) => {
  return bindFirestoreRef('users', 
      Firebase.firestore().collection('users').limit(8), { serialize })
}),
bindMoreUsers: firestoreAction(context => {
  return context.bindFirestoreRef('users', Firebase.firestore().collection('users').startAfter(context.state.users[context.state.users.length - 1]._doc).limit(8), { serialize })
})

Upon reaching the end of the page, I invoke bindMoreUsers to refresh the state.users with the subsequent 8 documents. My goal is to add to the state.users rather than replace the original set of 8 documents. How can I achieve this?

Answer №1

Full disclosure: I have yet to incorporate pagination into my current application, but I have a plan for how I would tackle the task.

In a previous response, I detailed how to maintain references to Firestore doc objects within each element of a state array bound by VuexFire or VueFire. In the first solution outlined below, we leverage these doc objects to implement Firestore's recommended cursor-based pagination for query result sets using the startAfter(doc) query condition, as opposed to the less efficient offset clause.

It's important to note that by utilizing Vuexfire/Vuefire, we are indicating a desire to receive live updates to our query, ensuring that our bound array reflects the desired data set accurately.

Solution #1: Pagination involves loading and displaying a horizontal slice of the entire dataset (maintaining a constant array size equal to the page size). While this doesn't align precisely with your initial request, it may be a preferable approach considering the drawbacks of other methods.

  • Pros: From the server standpoint, this pagination query will execute with minimal cost and delay for large datasets.
  • Pros: On the client side, it maintains a small memory footprint and renders quickly.
  • Cons: Pagination navigation may not resemble traditional scrolling behaviors, likely relying on buttons for forward and backward movement.
  • Page Forward: Retrieve the doc object from the last element of our state array and apply a startAfter(doc) condition to the refreshed query for advancing to the next page.
  • Page Backward: Requires a bit more effort. Obtain the doc object from the first element of the bound state array. Execute the page query with startAfter(doc), limit(1), offset(pagesize-1), and a reverse sort order. This yields the initial doc (pageDoc) of the preceding page. Then utilize startAfter(pageDoc), a forward sort order, and limit(pageSize) to rebind the state array (similar query to Page Forward but with doc = pageDoc).

Please note that in a broader context, it's debatable whether retaining pageDoc values from earlier pages (to avoid the reverse query) is viable. As we treat this as a 'live' updated filtered list, the number of remaining items from previous pages may have drastically changed since scrolling down. Depending on your specific application's expectations of rate of change, retaining past pageDoc values may prove advantageous.

Solution #2: Advancing pages enlarges the query result and bound array.

  • Pros: Offers a user experience akin to traditional scrolling as the array expands.

  • Pros: Eliminates the need for using a serializer workaround, as startAfter() or endBefore() are unnecessary.

  • Cons: Server-side, you're reloading the entire array up to the new page from Firestore each time you rebind for a new page and receiving real-time updates for a growing array. The multitude of doc reads could become costly.

  • Cons: Client-side rendering may slow down with page progression - although shadow DOM may provide a remedy. UI flickering during reloads may necessitate additional UI tricks to ensure seamless transition (e.g., delayed rendering until the array is fully updated).

  • Pros: Potentially well-suited for applications utilizing an infinite scrolling feature, though some testing may be warranted.

  • Page Forward: Increment the pageSize within the query limit and rebind, triggering a Firestore re-query and full reload.

  • Page Backward: Decrement the pageSize from the query limit and rebind/reload accordingly (or not!). May require adjusting the scroll position as well.

Solution #3: A hybrid of Solution #1 and #2. This approach involves utilizing live Vuexfire/Vuefire binding for a slice of the query/collection (as in Solution #1) and employing a computed function to concatenate it with an array already containing loaded data pages.

  • Pros: Reduces Firestore query costs and delays, maintaining a smooth scrolling experience conducive to Infinite scrolling UI implementation. Where's the Kool-Aid?
  • Cons: Requires meticulous tracking of the displayed array segment to ensure it remains bound and receives live updates.
  • Page Forward/Backward: Same process as Solution #1 for binding the current data page, except now there's a need to copy the previous page of data into the non-live array and devise a small computed function to concat() the two arrays for UI list binding.

Solution #3a: A slight workaround involves substituting invisible earlier data pages with a div of equivalent height ;) to maintain the appearance of scrolling continuity. As you scroll back, the concealed prior page div must be replaced with newly bound data. For seamless infinite scrolling UX, preloading an additional page ahead or behind is recommended to ensure timely loading well before reaching the page transition point. Not all infinite scroll APIs support this feature.

Solutions #1 & #3 may benefit from a Cookbook PR to VueFire or a promising MIT-licensed / NPM library. Any volunteers?

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Exploring locations with Google Maps and integrating them into interactive

I recently encountered an issue while working on a node express application and integrating the google maps javascript api. The problem arose when I attempted to transfer the sample code from the google website, along with my API key, into a .ejs file. S ...

Move the text to the following line if a horizontal scroll bar is visible using JavaScript/JQuery and CSS styling

I have a section with multiple div elements...how can I ensure that when there is horizontal scrolling in the section, the hidden divs shift to the next line? HTML <section id="a"> <div class="num"> <div class="num"> <div class="num" ...

I am trying to include the Css Baseline from @mui/material in my project. However, even though it is present in my JSON file, I am encountering an error stating that '@mui/material' needs to be included in the project

Struggling to import Css Baseline from @mui/material, it's listed in my json but I keep getting an error saying '@mui/material' should be included in the project's dependencies. I've been stuck on this issue for a while now! { &q ...

How come my dynamic source path doesn't function correctly unless I add an empty string at the end of it?

Recently, I encountered an issue while using Vue.js to dynamically create a source attribute using an object's properties. Here is the code snippet where I faced the problem: <img :src='"../assets/" + project.image.name + "." + project.image. ...

using vuejs to pass a function as a prop

As I work on creating a foundational "TableComponent," incorporating selectable rows and more, I am faced with the requirement for this TableComponent to accept a prop named "buttons." These buttons are expected to be in the form of an array of objects ...

Transmitting information to the main Vue class component

I'm facing an issue with a Vue component that I've defined using Vue class components. Here is the code snippet: @Component export default class MyComponent extends Vue { value = 0; } My requirement is to create multiple instances of this comp ...

Incorporate Vuetify's v-stepper seamlessly with Vue router for dynamic functionality

Seeking assistance in integrating vuetify's v-stepper with vue router. Specific requirements include: Assigning each step its own route (e.g. /myform/step1, /myform/step2, /myform/step3, etc) Creating components for each step that are dynamically lo ...

Authentication for REST API using JavaScript in the web browser

Currently, I am experimenting with creating a stateless, REST-based API that I intend to use from multiple sources, one of which will be a single-page Javascript web application. The goal is to have a unified API for different clients, even those developed ...

Sending emails with SMTP in JavaScript using the mailto form

I'm facing a challenge with my form. I am looking for a way to have the Send-email button trigger mailto without opening an email client, instead automatically sending via JavaScript (smtp). I'm not sure if this is achievable or if I'm askin ...

"Encountered a floating-point issue when trying to read an Excel file with

When a user uploads an Excel file that contains decimal, string, and Unicode characters, I am encountering an issue with floating point errors when reading certain decimal values. For instance, a number like 0.15 is being read as 0.150000000002 in some c ...

When an Axios response is received, the console logs an error message

Whenever I try to console.log(response) from json placeholder, an error message pops up stating that there is an "Unexpected console statement". Below is the code snippet: import axios from 'axios'; export default { name: 'app', ...

Reactivity in Vue on dynamically generated HTML elements

Experimenting with Vue 2, I attempted to generate DOM elements in the mounted hook as shown below: <div id="app"> <div id="container"> <label for="static_name">Existing field</label> <input ...

Iterate through the call feature repeatedly, ensuring that each call has a different iteration number assigned to a variable within the

I have a situation where I need to call a certain feature (which has validations) multiple times within a loop. Currently, my code successfully calls the feature 3 times. * def xxx = """ function(times){ for(i=0;i<times ...

What is the best way to retrieve the dimensions of an element using ReactNode?

                In my dynamic component, I am passing children as props with the following interface: interface Props { ...some props children: React.ReactNode } export default Layout({...some props, children}: Props) {...} Within the Layo ...

Identify the transition of the parent element containing the <iframe> from a hidden state to a

Is there a way to identify when an iframe is shown after being hidden? HTML : <div style="display:none"> <iframe></iframe> </div> When the <div> is displayed using jQuery with $('div').show();, how can I determi ...

Tips for successfully transferring a JSON object from jQuery to a JavaScript function

Can you help me with accessing data in a JavaScript function after populating it dynamically on an HTML page through an Ajax call? Issue: I am trying to invoke a JavaScript function when clicking on a button after populating the data. However, I am facing ...

What is the best way to incorporate the .then method in a promise?

I'm currently working on some JS Express code that looks like this: app.get('/lists2', (req, res) => { mongo.getDB() .then(db => db.collection('dogs')) .then(collection => collection.find().toArray()) .then ...

What could be the reason for the malfunction of this angular binding?

Looking at the HTML below: <input type="checkbox" name="person" [(ngModel)]="person.selected" /> This input is part of a ngFor loop. Testing has revealed that despite some values of selected being true and others false, all checkboxes appear check ...

What is the process of enabling scrolling on the main panel once scrolling on the sidebar has concluded?

How can I achieve a scrolling behavior similar to the one demonstrated here? When scrolling through the sidebar, I want it to continue scrolling until it reaches the end. After that, any further scrolling on the sidebar should scroll the main panel inste ...

Alter the language settings of the Datepicker feature in Material Angular 4

Need help changing the language of Datepicker in Material Angular. Struggling to locate this information in the Angular material 2 documentation. Check out this plunkr https://plnkr.co/edit/unzlijtsHf3CPW4oL7bl?p=preview <md-input-container> < ...