[英]React functional component re-rendering infinitely when using Axios in useCallback hook?
Here is a file uploading component, everything works at expected, however, when attempting to POST the file using Axios in a useCallback, the ProgressBar
component re-renders infinitely if there is an error from Axios.这是一个文件上传组件,一切正常,但是,当尝试在 useCallback 中使用 Axios 发布文件时,如果 Axios 出现错误,
ProgressBar
组件会无限重新渲染。 If I comment out the Axios post, the component does not re-render infinitely.如果我注释掉 Axios 帖子,组件不会无限重新渲染。 How do I avoid the infinite re-rendering of the
ProgressBar
component?如何避免
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
The component re-renders because filesUploaded
is modified in the callback each time and is listed as a dependency to the same callback.组件会重新渲染,因为
filesUploaded
每次在回调中都被修改,并且被列为对同一回调的依赖。 It looks like you wish to terminate the upload if the file already has been updated, but currently you only terminate the loadstart
event handler.如果文件已经更新,您似乎希望终止上传,但目前您只终止
loadstart
事件处理程序。 I suggest you move some of the functionality out from then loadstart
event.我建议您从
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]
})
}
[...]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.