Sean2014
Sean2014

Reputation: 531

How to synchronize setState with another function

The code below is the one that I'm currently working on. What it does is to receive a file as a user input and send an axios post request, and through Spring Boot backend the passed file will be stored in a folder in my PC's C drive.

import React from 'react'
import { post } from 'axios';

class SimpleFileUpload extends React.Component {

  constructor(props) {
    super(props);
    this.state ={
      file:null
    }
    this.onFormSubmit = this.onFormSubmit.bind(this)
    this.onChange = this.onChange.bind(this)
    this.fileUpload = this.fileUpload.bind(this)
  }

  onFormSubmit(e){
    e.preventDefault() // Stop form submit
    this.fileUpload(this.state.file).then((response)=>{
      console.log(response.data);
    })
  }
  onChange(e) {
    const chosenFile = e.target.files[0];
    this.setState({file:chosenFile});
  }
  fileUpload(file){
    const url = 'http://localhost:8080/report/uploadFile';
    const formData = new FormData();
    formData.append('file',file)
    const config = {
        headers: {
            'content-type': 'multipart/form-data'
        }
    }
    return  post(url, formData,config)
  }

  render() {
    return (
      <form onSubmit={this.onFormSubmit}>
        <h1>File Upload</h1>
        <input type="file" onChange={this.onChange} />
        <button type="submit">Upload</button>
      </form>
   )
  }
}

export default SimpleFileUpload

The problem was that setState was performed asynchronously within the onChange method and and when I clicked the Upload button to trigger the onFormSubmit method, the file inside this.state was null and axios.post request failed.

I managed to have the post request successfully work by modifying the onChange method like below, whose idea is something close to using a callback function,

onChange(e) {
  const chosenFile = e.target.files[0];
  this.setState({file:chosenFile});

  this.fileUpload(chosenFile).then((response)=>{
    console.log(response.data);
  })  
}

but the problem with this way is that the post request gets sent even without clicking the Upload button.

I tried to wrap the setState with an await feature by adding the two methods below,

setStateSynchronous(stateUpdate){
  return new Promise(
    this.setState({file:stateUpdate})
  )
}

async callSynchSetState(stateUpdate){
  await this.setStateSynchronous(stateUpdate);
}

and mofified the onChange method like below, but it didn't work.

onChange(e) {
  const chosenFile = e.target.files[0];
  //this.setState({file:chosenFile});
  this.callSynchSetState(chosenFile);
    
  /*this.fileUpload(chosenFile).then((response)=>{
    console.log(response.data);
  })  */
}

Any idea on how to send the chosen file only if the Upload button is clicked?

Upvotes: 1

Views: 82

Answers (1)

furitetepporaa
furitetepporaa

Reputation: 108

Your attempt to wrap the setState is not completely wrong. You just needed to write more succinctly.

Instead of

setStateSynchronous(stateUpdate){
  return new Promise(
    this.setState({file:stateUpdate})
  )
}

async callSynchSetState(stateUpdate){
  await this.setStateSynchronous(stateUpdate);
}

, just wrap the setState like so

async awaitSetState(stateUpdate){
    await this.setState({file:stateUpdate})
}

, and then just call that method in onChange method like so

onChange(e) {
  const chosenFile = e.target.files[0];
  this.awaitSetState(chosenFile);
}

Upvotes: 1

Related Questions