'React Dropzone - async function in onDrop callback doesn't update state
I'm trying to use react-dropzone onDrop with useCallback function to update the array of files dropped onto the area and trigger the upload. My code looks like this:
const [files, setFiles] = useState([]);
...
const onDrop = useCallback((acceptedFiles) => {
setFiles(acceptedFiles);
handleFileUpload();
}, []);
const {
getRootProps,
getInputProps,
isDragActive,
} = useDropzone({ onDrop });
handleFileUpload is an asynchronous function, which uses Fetch API. When I try to log the files array inside this method, it appears to be empty, even though I updated the state before running this function.
const handleFileUpload = async () => {
console.log(files); // <- returns empty array
}
I also tried to set different variables to indicate if the upload has started, etc. but changing the values within this method doesn't update the state of my component at all. Is it even possible to use an async function in a callback like this? Should I trigger the file upload somewhere else? I feel like I don't quite understand the concept here.
Solution 1:[1]
Your state will not update until after your code exits and/or relinquishes control. i.e. after you call setFiles() the files variable will still be STALE until the code returns to the event loop.
Just pass the accepted files into the function.
const onDrop = useCallback((acceptedFiles) => {
setFiles(acceptedFiles); // <-- that won't update right away
handleFileUpload(acceptedFiles);
}, []);
UPDATE
Here are snippets from one of my projects. I just stuff the dropped file into state, as you do. That causes the component to re-render, and when it does, I catch the updated file state in a hook, async parse the file, and return the data - which is then consumed by the component. The component shows UI states - importing if fileToImport is set and dataToImport is not yet available, etc...
// MyComponent.tsx
const MyComponent = () => {
const [fileToImport, setFileToImport] = useState<File | undefined>()
const [dataToImport, dataToImportError] = useReadFileData(fileToImport)
const onDrop = useCallback(acceptedFiles => {
setFileToImport(acceptedFiles[0])
}, [])
//useREadFileData.ts hook
import { useEffect, useState } from 'react'
const useReadFileData = (file: File | undefined): [any[], string | undefined] => {
const [rows, setRows] = useState<unknown[]>([])
const [error, setError] = useState<string>()
useEffect(() => {
async function parseFile(file: File) {
try {
// snip
setRows(rows)
} catch (error: any) {
if (typeof error === "object") {
setError(error?.message ?? "Error parsing file")
}
}
}
// only if we have a file to parse
if (file) {
parseFile(file)
}
}, [file, setError, setRows])
return [rows, error]
}
export default useReadFileData
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
