这是一个文件上传组件,一切都按预期工作,但是,当尝试在
useCallback
中使用 Axios POST 文件时,如果 Axios 出现错误,ProgressBar
组件将无限地重新渲染。如果我注释掉 Axios 帖子,该组件不会无限地重新渲染。如何避免 ProgressBar
组件无限重新渲染?
import { useState, useCallback, useEffect } from 'react'
import { useDropzone } from 'react-dropzone'
import uuid from 'react-uuid'
import axios from 'axios'
import ProgressBar from './ProgressBar'
const FileUploader = ({ setNotifications }) => {
const [fileCount, setFileCount] = useState(0)
const [filesUploaded, setFilesUploaded] = useState([])
const [progress, setProgress] = useState(0)
const [uploaded, setUploaded] = useState(false)
const [exists, setExists] = useState(false)
const [error, setError] = useState(false)
const onDrop = useCallback(acceptedFiles => {
acceptedFiles.forEach(file => {
const reader = new FileReader()
// console.log(file)
reader.onloadstart = () => {
const exists = filesUploaded.find(uploadedFile => uploadedFile.name === file.name)
if (exists) {
return setNotifications(notifications => {
return [...notifications, `'${file.name}' has already been uploaded.`]
})
}
// setStart(true)
return setFilesUploaded(filesUploaded => {
return [...filesUploaded, file]
})
}
reader.onabort = () => {
setError(true)
console.log('file reading was aborted')
}
reader.onerror = () => {
setError(true)
console.log('file reading has failed')
}
reader.onprogress = e => {
// console.log('loaded', e.loaded)
// console.log('total', e.total)
if (e.lengthComputable) {
setProgress((e.loaded / e.total) * 100)
}
}
reader.onload = async () => {
// complete
await axios.post(
'/api/images',
{
file: reader.result
}
)
.then(res => {
if (res) {
setUploaded(true)
if (res === 200) {
// success
setExists(false)
} else if (res === 409) {
// already exists
setExists(true)
}
}
})
.catch(err => {
setError(true)
console.error(err)
})
}
reader.readAsArrayBuffer(file)
})
}, [filesUploaded, setNotifications])
const { getRootProps, getInputProps } = useDropzone({ onDrop, multiple: true })
useEffect(() => {
setFileCount(filesUploaded.length)
}, [setFileCount, filesUploaded, setNotifications])
return (
<div>
<div className='file-uploader'>
<div
className='file-uploader-input'
{...getRootProps()}
>
<input {...getInputProps()} />
<p>Upload Files</p>
</div>
</div>
<div className='progress-bar-container'>
{filesUploaded.map(file => {
return (
<ProgressBar
key={uuid()}
file={file}
progress={progress}
uploaded={uploaded}
exists={exists}
error={error}
/>
)
})}
</div>
</div>
)
}
export default FileUploader
组件重新渲染,因为
filesUploaded
每次都会在回调中修改,并被列为同一回调的依赖项。如果文件已更新,您似乎希望终止上传,但目前您仅终止 loadstart
事件处理程序。我建议您从当时的 loadstart
事件中移出一些功能。
acceptedFiles.forEach(file => {
const exists = filesUploaded.find(uploadedFile => uploadedFile.name === file.name)
if (exists) {
setNotifications(notifications => {
return [...notifications, `'${file.name}' has already been uploaded.`]
})
} else {
const reader = new FileReader()
reader.onloadstart = () => {
return setFilesUploaded(filesUploaded => {
return [...filesUploaded, file]
})
}
[...]