Below is a FileUploader Component (parent) that renders ProgressBar's (children) as a file is added. I intend to pass in the FileReader onprogress so that the width of the progress bar goes from 0 to 100. To test I made a useEffect counter that goes from 0 to 100. I have confirmed that the counter state is updating by console.logging counter in the useEffect, however the progress state is not being changed in the ProgressBar component and only sends over the initial 0. How do I pass the counter state into the ProgressBar child so that it goes from 0 to 100?
import { useState, useCallback, useEffect } from 'react'
import { useDropzone } from 'react-dropzone'
const ProgressBar = ({ exists, file, progress }) => {
// only the first value, 0, of progress comes through, why not through 100?
console.log(progress)
return (
<div className='progress-bar-component'>
<div className='progress-bar-filename'>
<span>{ file.name }</span>
</div>
<div className='progress-bar-group'>
<div
className={ `progress-bar${exists.length ? ' progress-bar-exists' : ''}` }
style={ { width: `${progress}%` } }
>
</div>
<div className='progress-bar-percent'>
<span>{ exists.length ? 'Already uploaded' : `${progress}%` }</span>
</div>
</div>
</div>
)
}
const ProgressBars = ({ progressBars }) => {
return (
<div className='progress-bar-container'>
{progressBars}
</div>
)
}
const FileUploader = () => {
const [filesUploaded, setFilesUploaded] = useState([])
const [progressBars, setProgressBars] = useState([])
//
const [counter, setCounter] = useState(0)
const [start, setStart] = useState(false)
useEffect(() => {
if (start) {
const timer = counter < 100 &&
setInterval(() => {
setCounter(counter 1)
}, 500)
return () => clearInterval(timer)
}
}, [counter, start])
//
const onDrop = useCallback(acceptedFiles => {
acceptedFiles.forEach(file => {
const reader = new FileReader()
reader.onloadstart = () => {
return setFilesUploaded(filesUploaded => {
return [...filesUploaded, file]
})
}
reader.onabort = () => console.log('file reading was aborted')
reader.onerror = () => console.log('file reading has failed')
reader.onprogress = () => {
setStart(true)
return setProgressBars(progressBars => {
return [
...progressBars,
<ProgressBar
key={progressBars.length}
file={file}
exists={filesUploaded}
progress={counter}
/>
]
})
}
reader.onload = () => {
}
reader.readAsArrayBuffer(file)
})
}, [filesUploaded, setStart, counter])
const { getRootProps, getInputProps } = useDropzone({ onDrop, multiple: true })
return (
<div>
<div className='file-uploader'>
<div
className='file-uploader-input'
{...getRootProps()}
>
<input {...getInputProps()} />
<p>Upload Files</p>
</div>
</div>
<ProgressBars progressBars={progressBars} />
</div>
)
}
export default FileUploader
CodePudding user response:
You are calling inside a callback and the useEffect does not affect the rendering of that callback. Try calling outside the callback, in the return part of the parent component
Something like this:
return (
<div>
<div className='file-uploader'>
<div
className='file-uploader-input'
{...getRootProps()}
>
<input {...getInputProps()} />
<p>Upload Files</p>
</div>
</div>
<ProgressBar
key={progressBars.length}
file={file}
exists={filesUploaded}
progress={counter}
/>
<ProgressBars progressBars={progressBars} />
</div>