Nick Wild
Nick Wild

Reputation: 575

React: useState delay issues

I am trying to update state with the result of an await axios function, but the state is out of sync by one cycle. Here is the code...

 const Index=() => {
  const [fileSelected, setfileSelected]=useState(null)
  const [uploadId, setuploadId]=useState('')
  const [Fred, setFred]=useState('')
  const [fileName, setfileName]=useState('')
  const [fileType, setfileType]=useState('')
  const [uploadPC, setuploadPC]=useState()
  const [backendUrl, setbackendUrl]=useState('http://localhost:4000')

  useEffect(() => {
    console.log("inside useEffect: ", Fred);
  }, [Fred]);
  
  const fileHandler=async(e) =>{
    try {
      let fileSelected = document.getElementById('myfile').files[0]
      let fileName = fileSelected.name
      let fileType = fileSelected.type
      setfileSelected(fileSelected)
      setfileName(fileName)
      setfileType(fileType)
      console.log(fileSelected)
    } catch (err) {
      console.error(err, err.message) 
    }
  }

   const startUpload=async(e) =>{
    try {
      e.preventDefault()
      
      const params = {
        fileName: fileName,
        fileType: fileSelected.type
      };
      
      let resp = await axios.get(`${backendUrl}/start-upload`, { params })
      const { uploadId } = resp.data
      console.log(uploadId)// this is all OK
      setFred(uploadId)
      console.log(Fred)//"This displays the previous cycle: ",
      uploadMultipartFile()
    } catch (err) {
      console.log("startupload ",err)
    }
  }

   const uploadMultipartFile=async() =>{    
    try {
      console.log('Delayed access to the state in here',Fred)
      const fileSize = fileSelected.size
      const CHUNK_SIZE = 5000000 // 5MB
      const CHUNKS_COUNT = Math.floor(fileSize / CHUNK_SIZE) + 1
      let promisesArray = []
      let start, end, blob
      console.log(uploadId)
      for (let index = 1; index < CHUNKS_COUNT + 1; index++) {
        start = (index - 1) * CHUNK_SIZE
        end = (index) * CHUNK_SIZE
        blob = (index < CHUNKS_COUNT) ? fileSelected.slice(start, end) : fileSelected.slice(start)

        let getUploadUrlResp = await axios.get(`${backendUrl}/get-upload-url`, {
          params: {
            fileName: fileName,
            partNumber: index,
            uploadId: Fred,
          }
        })
etc

The correct uploadId is not available from within uploadMultipartFile which is causing big problems.

I have heard that I should useEffect, but not sure how to at the moment. I am refactoring from a class and that worked OK

Upvotes: 0

Views: 2078

Answers (2)

cbdeveloper
cbdeveloper

Reputation: 31315

See if this helps:

Can you move uploadMultipartFile() out of your component? Let the setState calls be made from startUpload.

const startUpload = async (e) => {
  try {
    // 1 - GATHER NECESSARY PARAMETERS HERE fileName uploadId etc...
    // 2 - CALL await uploadMultipartFile(...necessaryParameters)
    // 3 - SET NECESSARY STATE AFTER SUCCESSFUL UPLOAD
  }
  catch {
    // HANDLE ERRORS FROM UPLOAD CODE
  }
}

Could this solve your problem?

Upvotes: 0

wangdev87
wangdev87

Reputation: 8751

setFred is an asynchronous method, so you can't get the updated value of Fred immediately after setFred().

setFred(uploadId)
console.log(Fred) // This will console old state value of `Fred`

You should use useEffect with adding a Fred dependency to check updated value.

useEffect(() => {
  console.log(Fred)
  if (Fred) // This means if Fred is not default empty state.
    uploadMultipartFile()
}, [Fred])

Upvotes: 2

Related Questions