0

The react state value this.state.condition is getting passed to axios call , but this.state.condition is mutated only on the second attempt of execution of function generateReport(), so that this.state.condition is passed as an empty array to an axios call on first attempt of the execution of the function generateReport(). Is there any work-around or solution to the problem? The code is given below.

generateReport(){
      this.setState({statusMsg: ""});
      this.setState({loaderInitialStatus:"Processing..."})
      //this.isReq() ?
      console.log('this.state.selectedOption???',this.state.selectedOption); 
      if(this.state.selectedOption && this.state.selectedOption.length > 0) {
        let groups = []
        this.state.selectedOption.map((item) => {
          groups.push(item.value); 
        })
        this.setState(prevState => ({ 
          condition: [...prevState.condition, { name: "readByGroup", operator: "IN  ", value: groups }]
        }))
      }
      console.log('this.state.condition???', this.state.condition);

        this.props.getMetricsByContent(this.state.condition).then((data) => {
          this.setState({isLoader: false});
          if(data && Array.isArray(data) && data.length > 0){
            let csvContent = papa.unparse(data);
            this.download(csvContent, 'metrics.csv', 'text/csv;encoding:utf-8');
            this.setState({statusMsg: "File Downloaded successfully"})
          } else this.setState({statusMsg: "No records to download"})
        })

      //: null;

    }
Prem
  • 5,685
  • 15
  • 52
  • 95
  • 1
    Store the new `condition` object in variable instead and pass that in to the function. Or call `getMetricsByContent()` in the setState callback above it. Setting state is not synchronous – charlietfl Nov 23 '18 at 03:22
  • Can you wrap your axios call in a conditional? That way it won't execute until the 2nd execution of generateReport `if(this.state.condition) { ...axios call... }` – Shawn Andrews Nov 23 '18 at 03:24
  • where are you mutating `this.state.condition`? This can help https://stackoverflow.com/questions/36085726/why-is-setstate-in-reactjs-async-instead-of-sync/36087156 – Vivek Nov 23 '18 at 04:20

2 Answers2

0

your setting state meanwhile you checking the condition also, so your getting unpredictable output which is wrong. put your logic in callback of setState

please go through bellow code :

generateReport(){
      this.setState({statusMsg:"", loaderInitialStatus:"Processing..."}, this.callBack)
}
   
callBack=()=>{
    console.log('this.state.selectedOption???',this.state.selectedOption); 
    if(this.state.selectedOption && this.state.selectedOption.length > 0) {
    let groups = []
    this.state.selectedOption.map((item) => {
        groups.push(item.value); 
    })
    this.setState(prevState => ({ 
        condition: [...prevState.condition, { name: "readByGroup", operator: "IN  ", value: groups }]
    }))
    }
    console.log('this.state.condition???', this.state.condition);

    this.props.getMetricsByContent(this.state.condition).then((data) => {
        this.setState({isLoader: false});
        if(data && Array.isArray(data) && data.length > 0){
        let csvContent = papa.unparse(data);
        this.download(csvContent, 'metrics.csv', 'text/csv;encoding:utf-8');
        this.setState({statusMsg: "File Downloaded successfully"})
        } else this.setState({statusMsg: "No records to download"})
    })
}
Sunil Kumar
  • 420
  • 4
  • 13
0

The reason for the unexpected behaviour is because this.setState() is asynchronous and doesn't execute immediately as one expects. In this case when setState() is being invoked one after the other, it is not guaranteed that the first setState() executes before the next one. Also, react tries to combine the maximum possible setState() invocations into one there by reducing the virtual DOM re-rendering and lookup for changes.

So, in this case where we want the operations to be synchronous, we can pass a callback which will execute once the setState() is successful.

generateReport(){
    const { selectedOption } = this.state;
    if(selectedOption && selectedOption.length > 0) {
      let groups = []
      selectedOption.map((item) => {
        groups.push(item.value); 
      })
      this.setState(currStaleState => ({
        statusMsg: "", loaderInitialStatus:"Processing...",
        condition: [...currStaleState.condition, { name: "readByGroup", operator: "IN  ", value: groups }]
      }), () => {
        this.props.getMetricsByContent(this.state.condition).then((data) => {
        this.setState({isLoader: false});
        if(data && Array.isArray(data) && data.length > 0){
          let csvContent = papa.unparse(data);
          this.download(csvContent, 'metrics.csv', 'text/csv;encoding:utf-8');
          this.setState({statusMsg: "File Downloaded successfully"})
        } else this.setState({statusMsg: "No records to download"})
      })
      })
    } else {
      console.log('error : no option selected');
    }
}

Also, I have merged all the setState() invocations which looked independent. Also you should have a else condition in case nothing is selected, just in case it's not handled. because your state would be set to processing forever as it was set in the first line before.

Hope this solves the issue. Please comment in case of any issues :)

Leela Venkatesh K
  • 1,390
  • 1
  • 13
  • 26