Estoy tratando de recorrer una matriz y para cada elemento de una matriz quiero usar getDoc() y luego agregar los datos del documento a una nueva matriz. Básicamente para hacer esto:
useEffect(() => { let items = []; for(const cart_item of cartItems){ getDoc(doc(getFirestore(), 'tickets', cart_item.ticket_id)).then((ticket_item) => { items.push({...ticket_item.data()}); }); } console.log(items); }, [cartItems])
Sin embargo, la matriz de elementos aún está vacía después de un ciclo en el que lo registro. Creo que el problema es que el bucle no está esperando a que getDoc() devuelva algo y continúa. ¿Cómo hacer que espere a que termine getDoc()? He estado leyendo sobre async, espero pero todavía no entiendo cómo manejarlo.
Intente refactorizar su código con una función asíncrona como se muestra a continuación:
useEffect(() => { const getItems = async () => { // fetching all documents by mapping an array of promises and using Promise.all() const itemsDocs = await Promise.all(cartItems.map(c => getDoc(doc(getFirestore(), 'tickets', c.ticket_id))) // mapping array of document data const items = itemsDocs.map(i => i.data()) console.log(items); } getItems(); }, [cartItems])
Cuando llama a getDoc
, debe hacer una llamada al servidor Firestore para obtener sus datos, lo que puede llevar algún tiempo. Es por eso que no devuelve inmediatamente la instantánea del documento, sino que usa una devolución de llamada then()
que el SDK de Firestore llama cuando recupera los datos del servidor.
Este tipo de llamada asincrónica cambia el orden en que se ejecuta el código, lo cual es más fácil de ver si agregamos algo de registro al código:
console.log("Before loading data"); for(const cart_item of cartItems){ getDoc(doc(getFirestore(), 'tickets', cart_item.ticket_id)).then((ticket_item) => { console.log("Got data"); }); } console.log("After starting data load");
Luego ejecuta este código, la salida de registro i:
Antes de cargar datos
Después de iniciar la carga de datos
tengo datos
tengo datos
...
Probablemente esto no sea lo que esperaba inicialmente, pero explica perfectamente por qué su console.log(items)
muestra una matriz vacía: en ese momento, ninguna de las llamadas a items.push({...ticket_item.data()})
tiene ejecutar todavía, porque los datos aún se están cargando desde el servidor.
Me di cuenta de que Dharmaraj acaba de publicar una respuesta con el código para una solución funcional usando Promise.all
, por lo que también recomiendo revisar eso.