Set loading state before and after an action in a React class component

I have function which dispatched an action. I would like to display a loader before and after the action. I know that react composing the object passed to setState. the question is how can I update the property in async way:

handleChange(input) {
    this.setState({ load: true })
    this.props.actions.getItemsFromThirtParty(input)
    this.setState({ load: false })
}

Basically, it all worked great if I put this property as part of the application state (using Redux), but I really prefer to bring this property to the component-state only.


you can wrap the setState in a Promise and use async/await as below

setStateAsync(state) {
    return new Promise((resolve) => {
      this.setState(state, resolve)
    });
}

async handleChange(input) {
    await this.setStateAsync({ load: true });
    this.props.actions.getItemsFromThirtParty(input);
    await this.setStateAsync({ load: false })
}

Source: ASYNC AWAIT With REACT


Wrap the rest of your code in the callback of the first setState:

handleChange(input) {
  this.setState({
    load: true
  }, () => {
    this.props.actions.getItemsFromThirtParty(input)
    this.setState({ load: false })
  })
}

With this, your load is guaranteed to be set to true before getItemsFromThirtParty is called and the load is set back to false.

This assumes your getItemsFromThirtParty function is synchronous. If it isn't, turn it into a promise and then call the final setState within a chained then() method:

handleChange(input) {
  this.setState({
    load: true
  }, () => {
    this.props.actions.getItemsFromThirtParty(input)
      .then(() => {
        this.setState({ load: false })
      })
  })
}

Here's a typescript implementation of an "async-await" setState:

async function setStateAsync<P, S, K extends keyof S>(
  component: Component<P, S>,
  state:
    ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) |
    Pick<S, K> |
    S |
    null
) {
  return new Promise(resolve => component.setState(state, resolve));
}

The previous answers don't work for Hooks. In this case you get the following error when passing a second argument to setState

Warning: State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect().

As the error message says, you need to use useEffect instead in this case (see also this discussion for more detailed information)