I’m trying to download objects, store them in an array and save a copy of that array in state. I’m also trying to display content from the state array.
Here’s the component with the irrelevant parts excluded:
const UpdateProfile = () => {
const [userFiles, setUserFiles] = useState<any>([])
async function downloadFiles() {
try {
let newFileList: any = []
const downloadedFiles = await listAll(storageRef)
const downloadedFilesCopy = [...downloadedFiles.items]
downloadedFilesCopy.forEach(async (file: any) => {
let downloadedFileMetadata: any = await downloadMetadata(file)
downloadedFileMetadata.url = await findDownloadURL(file)
newFileList.push(downloadedFileMetadata)
})
console.log(newFileList)
console.log(userFiles)
setUserFiles((prev: any) => [...prev, ...newFileList])
console.log(userFiles)
} catch(e) {
alert(e)
}
}
async function downloadMetadata(item: StorageReference) {
const fileRef = ref(storage, item.toString())
try {
const metadata = await getMetadata(fileRef)
return metadata
} catch(e) {
alert(e)
}
}
async function findDownloadURL(item: StorageReference) {
const fileRef = ref(storage, item.toString())
try {
const url = await getDownloadURL(fileRef)
return url
} catch(e) {
alert(e)
}
}
useEffect(() => {
getProfileDetails()
downloadFiles()
}, [])
return (
<Container id="update-page-container">
{userFiles.map((file: any) => <p>File here</p>)}
</Container>
)
}
The state is updated correctly in the background but there is no re-render, even though calling setUserFiles with a new array each time (as far as I can tell).
Can anyone help me get React to re-render because I’m pulling my hair out.
Tried multiple ways to set state, e.g.
setUserFiles(newFileList)
setUserFiles([...newFileList])
2
Answers
Whats happening is that due to the asynchronous nature of your code (the download/for loop being async and the state update also being async, the
setUserFiles
might not be happening after the files have been downloaded. SpecificallydownloadedFilesCopy.forEach(async (file: any) ...
So what’s probably happening is
setUserFiles
update gets called, but files aren’t done downloading.setUserFiles
has already executed with the old file list.I believe you can
await Promise.all
on the download portion of your code to wait for all files to finish downloading before updating the state variable.Something like
Functions which are used in a useEffect, should be declared within useEffect. So, the
getProfileDetails
should be inside in useEffect.Additionally, promises in
Array.forEach
don’t work as expected, may cause buggy results, instead, better useArray.map
.