I am trying to make a button in my React web application which exports either all data or preferably specific collections from Firebase Cloud Firestore database as JSON.
Below is what I'm working with currently. This sort of works but not properly. When I click the button and download the JSON, it will be empty because the data wasn't loaded yet. When I click the button the second time and download the JSON again, it will have the desired data because it was loaded after the first click. I have tried different async and Promise structures to wait for all the data before starting the download but so far I have failed. The data could always be downloaded pre-hand with useEffect but this is not an option because it extremely increases the Firebase calls made with the application.
How do you properly wait for all the data before downloading it? Is there some different way to achieve this?
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>
)
}
CodePudding user response:
I don't know 100% if this will work, the fact is that setState
Hook is asyncronous, so even setDownload
will run before the click, the download state will be an empty array when the click()
event run.
My suggestion is to create an Hooks that listen to download state, and check if isn't empty and then fire the click event.
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>
)
}