Estoy tratando de crear un botón en mi aplicación web React que exporte todos los datos o preferiblemente colecciones específicas de la base de datos Firebase Cloud Firestore como JSON.
A continuación se muestra con lo que estoy trabajando actualmente. Este tipo de obras, pero no correctamente. Cuando hago clic en el botón y descargo el JSON, estará vacío porque los datos aún no se cargaron. Cuando haga clic en el botón por segunda vez y descargue el JSON nuevamente, tendrá los datos deseados porque se cargó después del primer clic. He probado diferentes estructuras async y Promise para esperar todos los datos antes de iniciar la descarga pero hasta ahora he fallado. Los datos siempre se pueden descargar previamente con useEffect, pero esta no es una opción porque aumenta enormemente las llamadas de Firebase realizadas con la aplicación.
¿Cómo espera correctamente todos los datos antes de descargarlos? ¿Hay alguna manera diferente de lograr esto?
function App() { const [download, setDownload] = useState([]) const downloadLink = useRef() const downloadMyCollection = async () => { const myCollection = collection(db, 'myCollection') const data = await getDocs(myCollection) setDownload(data.docs.map((doc) => ({...doc.data(), id: doc.id}))) downloadLink.current.click() } return ( <div> <button className="downloadButton" onClick={ downloadMyCollection }> Download Responses </button> <a href={`data:text/json;charset=utf-8,${encodeURIComponent( JSON.stringify(download) )}`} download='export.json' className='hidden' ref={downloadLink}> isHidden </a> </div> ) }
No sé al 100% si esto funcionará, el hecho es que setState
Hook es asíncrono, por lo que incluso setDownload
se ejecutará antes del clic, el estado de descarga será una matriz vacía cuando se ejecute el evento click()
.
Mi sugerencia es crear Hooks que escuchen el estado de descarga, verifiquen si no está vacío y luego activen el evento de clic.
function App() { const [download, setDownload] = useState([]) const downloadLink = useRef() const downloadMyCollection = async () => { const myCollection = collection(db, 'myCollection'); const data = await getDocs(myCollection); setDownload(data.docs.map((doc) => ({...doc.data(), id: doc.id}))) }; useEffect(() => { if(!!download.length) downloadLink.current.click(); },[download]} return ( <div> <button className="downloadButton" onClick={ downloadMyCollection }> Download Responses </button> <a href={`data:text/json;charset=utf-8,${encodeURIComponent( JSON.stringify(download) )}`} download='export.json' className='hidden' ref={downloadLink}> isHidden </a> </div> ) }