INTRODUCTION
J'ai une liste d'éléments avec la propriété "date" stockée dans mon FireStore. Dans le code client, j'ai une FlatList avec tous ces éléments classés par "date" (le premier élément est l'élément le plus récent, le second, l'élément que j'ai téléchargé avant l'élément qui apparaît en premier, ...).
Le problème est que je n'obtiens que 5 éléments (mais c'est parce que je ne veux pas obtenir 100 éléments en une seule fois), et je ne sais pas comment combiner cela avec onEndReached de FlatList (car c'est un agent listener qui doit être détaché lorsque le composant se démonte) pour obtenir plus d'éléments en suivant le même ordre.
Une idée sur la manière de faire fonctionner ce système ? J'ai commenté "<------------" sur les lignes du code que je pourrais être amené à modifier.
BASE DE DONNÉES FIRESTORE
Items -> user.uid -> userItems :
{
...
date: 1/1/1970
},
{
...
date: 2/1/1970
},
...
{
...
date: 31/1/1970
}
COMMENT MA FLATLIST DOIT ÊTRE RENDUE :
Les éléments de la FlatList dans l'ordre :
{ // The most recent one appears at the top of the list
...
date: 31/1/1970
},
...
{
...
date: 2/1/1970
},
{
...
date: 1/1/1970
},
CODE
const [startItem, setStartItem] = useState(null);
useEffect(() => {
const { firebase } = props;
let itemsArray = [];
// Realtime database listener
const unsuscribe = firebase // <------- With this I get the 5 most recent items when component mounts, or only one if the user has uploaded it after the component mounts
.getDatabase()
.collection("items")
.doc(firebase.getCurrentUser().uid)
.collection("userItems")
.orderBy("date") // Sorted by upload date <------------------
.startAfter(startItem && startItem.date) // <-----------------------
.limitToLast(5) // To avoid getting all items at once, we limit the fetch to 5 items <----------
.onSnapshot((snapshot) => {
let changes = snapshot.docChanges();
changes.forEach((change) => {
if (change.type === "added") {
// Get the new item
const newItem = change.doc.data();
// Add the new item to the items list
itemsArray.unshift(newItem);
}
});
// Reversed order so that the last item is at the top of the list
setItems([...itemsArray]); // Shallow copy of the existing array -> Re-render when new items added
setIsLoading(false);
// Change the start item
setStartItem(itemsArray[itemsArray.length - 1]);
});
return () => {
// Detach the listening agent
unsuscribe();
};
}, []);
...
<CardList data={items} isLoading={isLoading} onEndReached={/*how to call the function 'unsuscribe'? */} /> // <----------
Ce dont j'ai besoin, c'est de récupérer les 5 autres éléments les plus récents lorsque la fin de la liste est atteinte, puis de les ajouter à l'élément fond de la liste
MISE À JOUR (ma meilleure approche pour l'instant)
const [items, setItems] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [start, setStart] = useState(null);
const limitItems = 5;
const getItems = () => {
/*
This function gets the initial amount of items and returns a
real time database listener (useful when a new item is uploaded)
*/
const { firebase } = props;
// Return the realtime database listener
return firebase
.getDatabase()
.collection("items")
.doc(firebase.getCurrentUser().uid)
.collection("userItems")
.orderBy("date") // Sorted by upload date
.startAt(start)
.limitToLast(limitItems)
.onSnapshot((snapshot) => {
let changes = snapshot.docChanges();
let itemsArray = [...items]; // <------- Think the error is here
console.log(`Actual items length: ${itemsArray.length}`); // <-- Always 0 WHY?
console.log(`Fetched items: ${changes.length}`); // 5 the first time, 1 when a new item is uploaded
changes.forEach((change) => {
if (change.type === "added") {
// Get the new fetched item
const newItem = change.doc.data();
// Add the new fetched item to the head of the items list
itemsArray.unshift(newItem);
}
});
// The last item is at the top of the list
setItems([...itemsArray]); // Shallow copy of the existing array -> Re-render when new items added
// Stop loading
setIsLoading(false);
// If this is the first fetch...
if (!start && itemsArray.length) {
// Save the startAt snapshot
setStart(itemsArray[itemsArray.length - 1].date);
}
});
};
const getMoreItems = () => {
/*
This funciton gets the next amount of items
and is executed when the end of the FlatList is reached
*/
const { firebase } = props;
// Start loading
setIsLoading(true);
firebase
.getDatabase()
.collection("items")
.doc(firebase.getCurrentUser().uid)
.collection("userItems")
.orderBy("date", "desc")
.startAfter(start)
.limit(limitItems)
.get()
.then((snapshot) => {
let itemsArray = [...items];
snapshot.forEach((doc) => {
// Get the new fethed item
const newItem = doc.data();
// Push the new fetched item to tail of the items array
itemsArray.push(newItem);
});
// The new fetched items will be at the bottom of the list
setItems([...itemsArray]); // Shallow copy of the existing array -> Re-render when new items added
// Stop loading
setIsLoading(false);
// Save the startAt snapshot everytime this method is executed
setStart(itemsArray[itemsArray.length - 1].date);
});
};
useEffect(() => {
// Get a initial amount of items and create a real time database listener
const unsuscribe = getItems();
return () => {
// Detach the listening agent
unsuscribe();
};
}, []);
Avec ce code, je peux récupérer une quantité initiale d'articles la première fois, puis la quantité suivante lorsque j'atteins la fin de ma FlatList. Mais pour une raison quelconque, l'état n'est pas mis à jour dans le listener... donc quand un nouvel élément est téléchargé, tous les éléments que j'avais récupérés auparavant disparaissent de la FlatList et ils sont récupérés à nouveau quand la fin de la FlatList est atteinte.