Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 使用对象的子值来状态对象和子组件_Javascript_Reactjs_React Native - Fatal编程技术网

Javascript 使用对象的子值来状态对象和子组件

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, 档案:, 进展

我有一个Dropzone,允许多个并发上传,我想显示所有上传的进度

在我的Dropzone组件中,我有一部分状态是一个上传数组:

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]