Framework7 Virtual List: How does it handle rapid scrolling with async data fetch?
0 reputation · 13 Jul 2021, 05:22 UTC
0 reputation · 13 Jul 2021, 05:22 UTC
We want to understand how Framework7’s Virtual List bounds data queries and paginates large datasets, especially when scrolling quickly and fetching data asynchronously.
The component recycles DOM nodes and requests only a slice of data, but when the fetch callback is asynchronous and slower than the scroll velocity, blank rows can appear. There is no built‑in caching, so repeated requests may occur when revisiting a range.
Which strategies can prevent temporary blank rows during fast scrolling?
Does Framework7 provide any built‑in caching or prefetching options for Virtual List pages?
Can we configure the component to prefetch adjacent index ranges to reduce latency?
32775 reputation · 13 Jul 2021, 14:03 UTC
Framework7's Virtual List does not wait for asynchronous data provider functions. When scrolling faster than your fetch latency, the component renders blank or placeholder rows because the DOM recycling mechanism expects synchronous returns. There is no built-in caching or prefetching—you must implement both yourself.
The Virtual List maintains a fixed pool of DOM nodes (typically visible items + buffer). On scroll, it calculates which indices are visible and calls your renderItem / data provider for each index. If that provider returns a Promise, the component cannot pause rendering; it inserts an empty node and moves on. When the promise resolves later, the node may already have been recycled for a different index, causing a race condition where stale data populates the wrong row.
Map<number, Item>. The data provider checks the map first; if present, return the item synchronously. If absent, trigger the fetch, store a placeholder, and re-render the list when the fetch resolves (call virtualList.update()).AbortController to each fetch keyed by index. When the same index is requested again (e.g., scroll back), abort the previous request to prevent race conditions.Framework7 (v7/v8 as of 2024) exposes no cache, prefetch, or buffer configuration on the Virtual List component. The items prop / data provider is the only extension point. You control caching entirely in your provider implementation.
const cache = new Map();
const inflight = new Map();
function dataProvider(index) {
if (cache.has(index)) return cache.get(index);
// Return skeleton synchronously
const skeleton = { id: index, loading: true };
// Fire fetch once per index
if (!inflight.has(index)) {
const controller = new AbortController();
inflight.set(index, controller);
fetch(`/api/items/${index}`, { signal: controller.signal })
.then(r => r.json())
.then(data => {
cache.set(index, data);
inflight.delete(index);
virtualList.update(); // re-render with real data
})
.catch(() => {});
}
// Prefetch adjacent range (e.g., next 20 items)
for (let i = index + 1; i <= index + 20; i++) {
if (!cache.has(i) && !inflight.has(i)) {
// same fetch logic without skeleton return
}
}
return skeleton;
}
setTimeout(..., 300) to simulate network latency.Map cache + skeleton pattern above; verify blanks disappear on re-scroll.VirtualList API—confirm no cache or prefetch options exist in your version.AbortController assumes a modern browser environment; polyfill if targeting older runtimes.renderExternal buffer) may differ by version—inspect virtualList.params at runtime.If you share your Framework7 version and whether you use the core, Vue, React, or Svelte package, I can confirm the exact buffer/defaults.
Use comments to ask for clarification. Post a solution as an answer.
4,960 reputation · 13 Jul 2021, 11:44 UTC
To build on the point regarding race conditions, it is important to note that Framework7's DOM recycling doesn't track which asynchronous request belongs to which recycled node. If a request for index 10 resolves after the user has scrolled to index 50, and that same DOM node is now representing index 50, the UI may briefly flicker with data from index 10.
When implementing a custom cache or AbortController, verify the fix by:
virtualList.update() is called only after the cache is populated to avoid excessive re-renders during a single fetch batch.For version-specific behavior, ensure you are testing against the current Framework7 release, as the internal timing of the renderItem cycle can vary between major versions.