【发布时间】:2020-10-20 17:37:59
【问题描述】:
我有一个允许多个并发上传的 Dropzone,我想显示所有上传的进度。
在我的 Dropzone 组件中,我有一部分状态是上传数组:
const [uploads, setUploads] = useState([])
uploads 数组的每个元素都是一个包含几个值的上传对象,如下所示:
const uploading = {
id: 1,
files: <array of files>,
progress: 0
}
一旦文件/文件夹被拖放到 dropzone 中,“uploading”对象将被添加到“uploads state array”中,并且文件将被发送到后端 API,后端 API 将文件异步上传到服务器。
后端会定期向 UI 发送进度回调,以更新上传状态数组中正确元素的进度值(请参阅下面的 progressCallback)
我目前无法实现的是确保每次上传数组中的对象被更新以显示进度时 UI 都会重新渲染,这样我就可以在所有上传发生时显示它们的进度。
UI 组件如下所示:
export function UploaderDropzone(props) {
const [uploads, setUploads] = useState([])
const progressCallback = useCallback((progressObject, sessionContext, step) => {
const {uploadSessionParameters} = sessionContext
let uploading = {}
// let tmpArray = []
const tmpArray = [...uploads]
if (step === 'progress') {
const filtered = findUploadById(tmpArray, uploadSessionParameters.uploadSessionId)
uploading = filtered[0]
if (uploading) {
const itemIndex = tmpArray.indexOf(uploading)
tmpArray.splice(itemIndex, 1)
uploading.progress = progressObject.percentUpload
tmpArray.push(uploading)
setUploads(tmpArray)
// setUploads(prevUploads => [...prevUploads, uploading])
}
console.log('progress tmpArray = ' + JSON.stringify(tmpArray));
console.log('progress uploads = ' + JSON.stringify(uploads))
}
if (step === 'initialize') {
const uploadNumber = uploads.length + 1
uploading = {
uploadSessionId: uploadSessionParameters.uploadSessionId,
files: sessionContext.files,
uploadNumber: uploadNumber,
uploadName: `Upload #${uploadNumber}`,
sent: false,
progress: 0,
}
tmpArray.push(uploading)
setUploads(tmpArray)
console.log('initialize tmpArray = ' + JSON.stringify(tmpArray))
console.log('initialize uploads = ' + JSON.stringify(uploads))
}
}, [uploads])
const progressBars = uploads.map((upload) => {
return (
<Fragment>
<ProgessBar progress={upload.progress} />
</Fragment>
)
})
// ... more code here ... not required for understanding
return {
<Fragment>
<Dropzone
onDrop={
acceptedFiles => {
const filteredFiles = acceptedFiles.filter((file) =>
validateFile(file))
console.log("Filtered files" + filteredFiles)
if (filteredFiles.length > 0) {
setAlertMsg('')
}
else {
setAlertMsg('No files uploaded.')
}
// call to Node.js backend, passing it the progressCallback
startAsyncUploadSession(filteredFiles, progressCallback);
}
}
/>
{progressBars}
</Fragment>
}
}
ProgressBar 组件非常简单:
export function ProgressBar(props) {
const {progress} = props
return (
<Fragment>
<p>`${progress}% uploaded ...`</p>
</Fragment>
)
}
现在,即使上传状态数组在 progressCallback 中不断更新,这段代码甚至都不会显示进度条。由于我不知道将要完成的并发上传数量,因此我无法在高阶组件中设置状态并将其作为道具传递,我需要子组件(ProgressBar)将其作为道具从多个对象中接收状态数组......但我显然错过了一些东西......
任何指针?我可以使用任何挂钩来在上传状态数组中注册对象的进度值,以便每次后端更新我们的进度时,它都会反映在 UI 中?
编辑:包含@Robin Zigmond 建议的部分修复
Edit2:经过一些调试,似乎是同步问题。所以我需要在这里添加一些代码和细节。
当文件被拖放到 Dropzone 时,它通过对模拟服务器的函数调用将文件发送到 Node.js 后端,调用 startAsyncUploadSession(filteredFiles, progressCallback);在 Dropzone 的 onDrop 事件中(使用 react-dropzone 库)。
似乎当我稍后调用 progressCallback 时,状态与第一次渲染时一样,也就是上传状态数组是一个空数组,就像文件被删除时一样,而不是包含添加对象的更新数组在“初始化”步骤中上传数组。
因此修改后的问题是“如何确保在稍后后端调用 progressCallback 时 UI 状态是最新的?”
【问题讨论】:
标签: javascript reactjs react-native