0

In my program I have to call setState in an arrow function. The function should set the state of isSummaryAccordionActive to true, so that another function can be called. However when I set the state in an arrow function the state doesn't change. it still reads as false in the console.

BELOW IS THE CODE:

    class TabMenuButtons extends Component {
        constructor(props) {
            super(props);
            this.state = {
                rendersummaryAccordions: false,
                renderservicesAccordions: false,
                rendertravelAccordions: false, 
                renderratesAccordions: false, 


//These variables used for other components to detect which component is currently rendered ("Active")
                isSummaryAccordionActive: false, 
                isServicesAccordionActive: false, 
                isTravelAccordionActive: false, 
                isRatesAccordionActive: false, 

            };

        }




    //The Summary Button onClick calls this function to set the state of the summaryAccordionDetector variable
    setStateisSummaryAccordionsActive = () => {
        this.setState({isSummaryAccordionActive: true})
        this.callSummaryAccordionsLogicGate();
        //WHERE THE STATE IS READING AS FALSE IN THE CONSOLE*****
        console.log(this.state.isSummaryAccordionActive)
        console.log("setStateisSummaryAccordionsActive Was called")
    }

    //Then the function above  calls this function that checks if the summaryAccordionDetector variable is really true 
    callSummaryAccordionsLogicGate = () => {
        if (this.state.isSummaryAccordionActive) {
           this.summaryAccordionsLogicGate();

        }
        else {
            // this.setState({isSummaryAccordionActive: false})
            console.log("callSummaryAccordionsLogicGate found that the summary accordion wasn't active")
        }
    }

    //If the function above verifies that the summaryAccordionDetector is really true it calls this function which renders the summary accordion
       summaryAccordionsLogicGate = () => {
            this.setState({rendersummaryAccordions: true});
            console.log("summaryAccordionsLogicGate was called, render the summary accordion")
    } 

    //   servicesAccordionsLogicGate = () => {
    //     this.setState({renderservicesAccordions: true});
    //     console.log("servicesAccordionsLogicGate was called")

    //   } 

    //   ratesAccordionsLogicGate = () => {
    //     this.setState({renderratesAccordions: true});
    //     console.log("ratesAccordionsLogicGate was called")

    //   } 

    //   travelAccordionsLogicGate = () => {
    //     this.setState({rendertravelAccordions: true});
    //     console.log("travelAccordionsLogicGate was called")

    //   } 



        render() {

            let summaryAccordionPlaceHolder = null
            let servicesAccordionPlaceHolder = null
            let ratesAccordionPlaceHolder = null
            let travelAccordionPlaceHolder = null

            this.state.rendersummaryAccordions ? summaryAccordionPlaceHolder = <SummaryAccordions/> : summaryAccordionPlaceHolder = null;
            this.state.renderservicesAccordions ? servicesAccordionPlaceHolder = <ServicesAccordions/> : servicesAccordionPlaceHolder = null;
            this.state.renderratesAccordions  ? ratesAccordionPlaceHolder = <RatesAccordions/> : ratesAccordionPlaceHolder = null;
            this.state.rendertravelAccordions  ? travelAccordionPlaceHolder = <TravelAccordions/> : travelAccordionPlaceHolder = null;

            return (
                <div>
                    <center>
                        <table cellspacing="30px">
                            <tr>
                                <td>
                                <Button label="ITEM" icon="pi pi-home"  className="TabMenuButtons" onClick={this.setStateisSummaryAccordionsActive}   style={{marginRight:'.25em', borderRadius: '8%',  backgroundColor: "#1c479c" }}></Button>
                                </td>
                                <td>
                                <Button label="ITEM" icon="pi pi-users"  className="TabMenuButtons" onClick={this.servicesAccordionsLogicGate}    style={{marginRight:'.25em', borderRadius: '8%',  backgroundColor: "#1c479c"}}></Button>
                                </td>
                                <td>
                                <Button label="ITEM" icon="pi pi-cloud"  className="TabMenuButtons" onClick={this.travelAccordionsLogicGate}  style={{marginRight:'.25em', borderRadius: '8%',  backgroundColor: "#1c479c"}}></Button>
                                </td>
                                <td>
                                <Button label="ITEM" icon="pi pi-money-bill" className="TabMenuButtons" onClick={this.ratesAccordionsLogicGate}   style={{marginRight:'.25em', borderRadius: '8%', backgroundColor: "#1c479c"}}></Button>
                                </td>
                            </tr>
                        </table>
                    </center>
                     <tr>

                        {/* EDIT THIS to become dynamic */}
                        <td className="StaticTextBelowTabView"><h1>  ITEM: <em>$67,000.00 </em></h1> </td>
                        <td className="StaticTextBelowTabView"><h1> ITEM: <em>$5,000.00</em>  </h1></td>
                        <td className="StaticTextBelowTabView"><h1> ITEM: <em>$54,406.00</em> </h1></td>
                        <td className="StaticTextBelowTabView"><h1>  ITEM: <em>1,000</em> </h1></td>
                        <td className="StaticTextBelowTabView"><h1> ITEM: <em>20.00%</em></h1></td>
                    </tr>
                    {ratesAccordionPlaceHolder}
                    {servicesAccordionPlaceHolder}
                    {travelAccordionPlaceHolder}
                    {summaryAccordionPlaceHolder}
                </div>
            );
        }

    }

I am new to reactJS is there something fundamentally wrong with my logic?

EDIT: Thank you all for the detailed responses! I really didn't know this about state until you all mentioned it. Looks like I'll have to read over the API reference in the future.

JavaHava
  • 189
  • 2
  • 15
  • 2
    I think setState is async, so that a log right after may not catch the state update. Try using the state to render something on to the screen. – Dan Jul 26 '19 at 16:11
  • 1
    @Dan is right `setState` is async and will not log the new state in the console. I would suggest you use React Dev Tools to inspect the component state and you will see the state update there. – T. Evans Jul 26 '19 at 16:12
  • 1
    Possible duplicate of [Why calling react setState method doesn't mutate the state immediately?](https://stackoverflow.com/questions/30782948/why-calling-react-setstate-method-doesnt-mutate-the-state-immediately) – Emile Bergeron Jul 26 '19 at 16:18

1 Answers1

2

What you need to understand is that this.setState is an asynchronous operation, it happens behind the scenes at a later time interval thus making it unpredictable to know when the state will get updated. That's why when you call callSummaryAccordionsLogicGate the value for isSummaryAccordionActive is still false

Here's a possible solution,


    setStateisSummaryAccordionsActive = () => {

      // Get the original value before setting the state
      const isSummaryAccordionActive=this.state.isSummaryAccordionActive;

      this.setState({isSummaryAccordionActive: true})

      // Pass the negation of the original value to the "callSummaryAccordionsLogicGate" function
      this.callSummaryAccordionsLogicGate(!isSummaryAccordionActive);

    }

    // Check with the parameter "isSummaryAccordionActive" which will have the correct value you expect
    callSummaryAccordionsLogicGate = (isSummaryAccordionActive) => {
      if (isSummaryAccordionActive) {
         this.summaryAccordionsLogicGate();

      }
      else {
          // this.setState({isSummaryAccordionActive: false})
          console.log("callSummaryAccordionsLogicGate found that the summary accordion wasn't active")
      }

Ramaraja
  • 2,526
  • 16
  • 21
  • 2
    I'd like to add that `this.setState` will trigger a re-render, and the state is only updated in that re-render. Calling `this.setState` then doing some synchronous actions with `this.state` right afterwards is one of the most common mistakes made by developers who are new to ReactJS. – Huy Jul 26 '19 at 16:22
  • @Huy Correct! Thank you for adding that crucial information. – Ramaraja Jul 26 '19 at 16:26
  • 2
    what @Ramaraja Ramanujan is correct, but I would like to add, that you can access the new state after setState with a callback as second parameter of setState: this.setState({isSummaryAccordionActive: true},() => console.log(this.state.isSummaryAccordionActive)}, which will be true now. – Domino987 Jul 26 '19 at 16:29
  • @Domino987 Yes, that's correct. As the [API reference](https://reactjs.org/docs/react-component.html#setstate) states ```setState(updater[, callback])```, the second argument is a callback function that executes after the state has been updated and thus we see the behaviour you have described. However, for the problem at hand, the approach given in the answer is more feasible because the callback doesn't get executed immediately and callbacks are hell :P – Ramaraja Jul 26 '19 at 16:48
  • Oh i agree, this was only added because OP apparently did not know it :) – Domino987 Jul 26 '19 at 16:50
  • @Ramaraja Ramanujan I'm currently implementing your solution and will reply to tell you how it goes – JavaHava Jul 26 '19 at 17:28