1

I'm creating a picture converter where my JS code gets the file inputs from the user, sends it to my python back-end where they are converted and saved to a folder. Python then sends a response back to JS (react), which updates the state for each file individually as "converted" and re-renders the necessary components.

I have a for loop that sends individual POST requests for each file. This is fine until I want to create a .zip for the entire directory after all files have been converted. My problem lies there. My zip is always returned empty or with incomplete files.

// function which takes the file inputs from user
uploadBatch = async () => {
  const files = this.getFilesFromInput();
  const batch = Math.floor(Math.random() * 999999 + 100000);
  for (let i = 0; i < files.length; i++) {
    // sets the state which will then be updated
    await this.setState(
      {
        files: [
          ...this.state.files,
          {
            // file state values
          }
        ]
      },
      () => {
        const formData = new FormData();
        // appends stuff to form data to send to python
        axios
          .post('/api/upload', formData, {
            headers: {
              'Content-Type': 'multipart/form-data'
            },
            responsetype: 'json'
          })
          .then(response => {
            // update the state for this particular file
          });
      }
    );
  }
  return batch;
};

// function which zips the folder after files are converted
handleUpload = async e => {
  e.preventDefault();
  // shouldn't this next line wait for uploadBatch() to finish before 
  // proceeding?
  const batch = await this.uploadBatch();
  // this successfully zips my files, but it seems to run too soon
  axios.post('/api/zip', { batch: batch }).then(response => {
    console.log(response.data);
  });
};

I have used async/await but I don't think I've used them well. I don't quite fundamentally understand this concept so an explanation would be greatly appreciated.

3
  • async await documentation - note, I doubt this.setState returns a Promise - which is what await awaits Commented Nov 20, 2018 at 2:15
  • @Bravo no, it just sets the state. I have read multiple documentations but am still not quite how or where exactly I'd include a promise Commented Nov 20, 2018 at 2:28
  • Possible Duplicate of What if you can use async/await to make React's setState synchronous Commented Nov 20, 2018 at 5:14

1 Answer 1

1

Whenever you call setState(), the component will re-render. You should ideally complete all your actions and call setState() at the end.

Something like this should get things working for you

// function which takes the file inputs from user
uploadBatch = async () => {
  const files = this.getFilesFromInput();
  const batch = Math.floor(Math.random() * 999999 + 100000);
  const files = [];
  for (let i = 0; i < files.length; i++) {
    const formData = new FormData();
    // appends stuff to form data to send to python
    const res = 
      await axios
        .post('/api/upload', formData, {
          headers: {
            'Content-Type': 'multipart/form-data'
          },
          responsetype: 'json'
        });

    files.push('push data into files arr');
  }

  return { files, batch };
};

// function which zips the folder after files are converted
handleUpload = async e => {
  e.preventDefault();
  // get batch and files to be uploaded and updated
  const { files, batch } = await this.uploadBatch();
  // this successfully zips my files, but it seems to run too soon
  await axios.post('/api/zip', { batch: batch }).then(response => {
    console.log(response.data);
  });

  // set the state after all actions are done
  this.setState( { files: files });
};
Sign up to request clarification or add additional context in comments.

5 Comments

I actually want to re-render each time since I want to display the file status for each file being uploaded (converting... -> converted). As soon as each file is converted, Python sends a response back to which then react updates the state. The problem is that I need handleUpload to wait for all the states to be converted before sending the zip request.
Then you need to store everything in state, say batch, allFilesUploaded and update the status of each file after the upload. You will need to use allFilesUploaded state value to trigger the /api/zip POST call.
I thought about that, and that would work for sure. But wouldn't using async/await be a better approach to this?
setState() will trigger a re-render every time it's executed and you'll never get to the end of the method even if you use async-await. You cannot continue a logic after updating the state by holding off render.
I wasn't holding off any render! I just wanted to wait for the for loop to finish before running another post request. Regardless, I fixed it with async-await as I suspected would work. I just had to include promises and tell my code to wait for those. I'll answer my own question tomorrow for others who might have this issue! Thanks for the help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.