1

I have a few methods in my React component, all of which modify state:

class Abc extends React.Component {

    state = {
        f1 : 'f1',
        f2 : 'f2',
        f3 : 'f2',
        dynamicValue : 'some initial value',
    }

    func1 = () => { 
        //..do some work
        this.setState({ f1 : someValue1})
    }

    func2 = () => { 
        //..do some work
        this.setState({ f2 : someValue2})
    }

    func3 = () => { 
        //..do some work
        this.setState({ f3 : someValue3})
    }

    doWorkAfterAllSetStateIsComplete = () => {
        const val = this.state.dynamicValue;
        // I get stale state here
    }

    doWork = () => {
        func1();
        func2();
        func3();
        doWorkAfterAllSetStateIsComplete();
    }
}

If I call doWorkAfterAllSetStateIsComplete in a setTimeout like this, I get the updated state.

setTimeout(() => {
    doWorkAfterAllSetStateIsComplete();
    // this.state.dynamicValue is updated here.
}, 0)

I know this is happening because setState is async and setTimeout calls doWorkAfterAllSetStateIsComplete in the next 'tick' of JavaScript, and so I get the updated state value inside doWorkAfterAllSetStateIsComplete. But this seems a little hacky to me. Is there another way to achieve this?

darKnight
  • 5,651
  • 13
  • 47
  • 87

2 Answers2

2

You can make your doWork function async so you wait for setStates to be done and then use their values in the function:

doWorkAfterAllSetStateIsComplete = () => {
  console.log(this.state);
}

doWork = async () => {
    await this.func1();
    await this.func2();
    await this.func3();
    this.doWorkAfterAllSetStateIsComplete();
}

You can also use componentDidUpdate() to check if state has changed or not:

componentDidUpdate(prevProps, prevState){
    if(prevState.f1 !== this.state.f1 && prevState.f2 !== this.state.f2 && prevState.f3 !== this.state.f3)
        this.doWorkAfterAllSetStateIsComplete();
}
Majid Amiri
  • 580
  • 4
  • 14
  • 1
    This is technically not correct, because `setState` does not return a promise, and is not awaited in the sample code either. Your would need to promisify and await `setState` to make this answer work. Check https://stackoverflow.com/questions/53409325/does-this-setstate-return-promise-in-react – Nappy Sep 01 '19 at 15:15
0

You could react to the state change in componentDidUpdate like this:

componentDidUpdate(prevProps, prevState) {
  if (this.state.f3 === someValue3) {
    doWorkAfterAllSetStateIsComplete();
  }
}

Alternatively you can provide a callback in setState to run on completion. In order to await all you could turn it to an Promise with promisify and await all those promises.

Nappy
  • 3,016
  • 27
  • 39