在 useCallback 钩子中使用 Axios 时,React 函数组件无限重新渲染?

问题描述 投票:0回答:1

这是一个文件上传组件,一切都按预期工作,但是,当尝试在

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
javascript reactjs react-hooks axios
1个回答
1
投票

组件重新渲染,因为

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]
      })
    }
    [...]
© www.soinside.com 2019 - 2024. All rights reserved.