Javascript 使用对象的子值来状态对象和子组件
我有一个Dropzone,允许多个并发上传,我想显示所有上传的进度 在我的Dropzone组件中,我有一部分状态是一个上传数组:Javascript 使用对象的子值来状态对象和子组件,javascript,reactjs,react-native,Javascript,Reactjs,React Native,我有一个Dropzone,允许多个并发上传,我想显示所有上传的进度 在我的Dropzone组件中,我有一部分状态是一个上传数组: const [uploads, setUploads] = useState([]) uploads数组的每个元素都是一个具有几个值的upload对象,如下所示: const uploading = { id: 1, files: <array of files>, progress: 0 } const上传={ id:1, 档案:, 进展
const [uploads, setUploads] = useState([])
uploads数组的每个元素都是一个具有几个值的upload对象,如下所示:
const uploading = {
id: 1,
files: <array of files>,
progress: 0
}
const上传={
id:1,
档案:,
进展:0
}
将文件/文件夹放入dropzone后,“上载”对象将添加到“上载状态数组”,并将文件发送到后端API,后者将文件异步上载到服务器
后端将定期向UI发送进度回调,以更新uploads状态数组中正确元素的进度值(请参阅下面的progressCallback)
我目前无法实现的是确保每次更新uploads数组中的对象时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>
}
}
导出功能上传道具(道具){
const[uploads,setUploads]=useState([])
const progressCallback=useCallback((progressObject,sessionContext,step)=>{
常量{uploadSessionParameters}=sessionContext
让上传={}
//让tmpArray=[]
const tmpArray=[…上传]
如果(步骤==‘进度’){
const filtered=findUploadById(tmpArray,uploadSessionParameters.uploadSessionId)
上传=已过滤[0]
如果(上传){
const itemIndex=tmpArray.indexOf(上传)
tmpArray.拼接(项目索引,1)
upload.progress=progressObject.percentUpload
tmpArray.push(上传)
设置载荷(tmpArray)
//setUploads(prevUploads=>[…prevUploads,Upload])
}
log('progress tmpArray='+JSON.stringify(tmpArray));
log('progress uploads='+JSON.stringify(uploads))
}
如果(步骤=='initialize'){
const uploadNumber=uploads.length+1
上传={
uploadSessionId:uploadSessionParameters.uploadSessionId,
文件:sessionContext.files,
uploadNumber:uploadNumber,
uploadName:`Upload#${uploadNumber}`,
发送:错误,
进展:0,
}
tmpArray.push(上传)
设置载荷(tmpArray)
log('initializetmparray='+JSON.stringify(tmpArray))
log('initializeuploads='+JSON.stringify(uploads))
}
},[上传])
const progressbar=上传.map((上传)=>{
返回(
)
})
//…此处有更多代码…不需要理解
返回{
{
const filteredFiles=acceptedFiles.filter((文件)=>
验证文件(文件))
log(“过滤文件”+过滤文件)
如果(filteredFiles.length>0){
setAlertMsg(“”)
}
否则{
setAlertMsg('未上载任何文件')
}
//调用Node.js后端,将progressCallback传递给它
StartSyncupLoadSession(FilteredFile、progressCallback);
}
}
/>
{progressBars}
}
}
ProgressBar组件非常简单:
export function ProgressBar(props) {
const {progress} = props
return (
<Fragment>
<p>`${progress}% uploaded ...`</p>
</Fragment>
)
}
导出功能进度条(道具){
const{progress}=props
返回(
`${progress}%已上载`
)
}
现在,即使uploads状态数组在progressCallback中不断更新,这段代码甚至没有显示进度条。由于我不知道将要完成的并发上载的数量,我无法在高阶组件中设置状态并将其作为道具传递,我需要子组件(ProgressBar)将其作为道具从状态数组中的多个对象接收。。。但我显然错过了一些东西
有什么建议吗?我是否可以使用任何钩子在uploads状态数组中注册对象的进度值,以便每次后端更新我们的进度时,它都会反映在UI中
编辑:包括@Robin Zigmond建议的部分修复
Edit2:经过一些调试后,似乎出现了同步问题。所以我需要在这里添加一些代码和细节
当文件被放入Dropzone时,its通过对模拟服务器的函数调用,即对startAsyncUploadSession(filteredFiles,progressCallback)的调用,将文件发送到Node.js后端;在Dropzone的onDrop事件中(使用react Dropzone库)
看起来,当我稍后调用progressCallback时,状态与第一次渲染时一样,aka uploads state array是一个空数组,与删除文件时一样,而不是包含在“初始化”步骤中添加到uploads array的对象的更新数组
因此,修改后的问题是“当后端稍后调用progressCallback时,如何确保UI状态是最新的?”问题在于您的状态更新了
progressCallback中的代码。以下是违规代码,供参考:
const tmpArray = uploads
const itemIndex = tmpArray.indexOf(uploading)
tmpArray.splice(itemIndex, 1)
// HERE UPDATING ONE OF ITEM'S VALUES IN UPLOADS STATE ARRAY
uploading.progress = progressObject.percentUpload
tmpArray.push(uploading)
setUploads(tmpArray)
它的作用是:
将tmpArray
设置为与当前状态相同的对象(uploads
)的引用
然后对数组进行变异,首先将一个元素剪接出来,然后将一个新元素推到数组上
在步骤2)中,参考值没有发生任何变化。因此,当您随后调用setUploads(tmpArray)
-这也可能是setUploads(uploads)
,因为这两个变量仍然是对完全相同数组的引用-React认为您正在将状态设置为原来的状态,因此不知道重新渲染
这是很长的一段路来解释为什么你永远不应该变异状态,就像你在这里做的那样。您需要对其进行不变的更新—也就是说,不要使用旧状态,构建一个新的对象/数组,并将其传递给用户
const tmpArray = uploads
const tmpArray = [...uploads]