简体   繁体   中英

setState callback not working as expected

I have a working app and am just trying to set the state to loading: true while my app makes a submission call so I can show a loading screen. Because I want to make sure I set the state before making the loading call, I use a callback. However I'm not seeing my loading update with the code below:

submitSecurityAnswer = () => {
    const { submit, handleError, navigate } = this.props;
    const { answer } = this.state;

    this.setState({ loading: true }, () => {
      console.log('setState', this.state)
      try {
        submit({ answer, ...this.props }).then(({ errors, status }) => {
          this.setState({ answer: '' });
          if (status && status === 200) {
            navigate();
          } else if (status === 401) {
            this.setState({ showError: true });
          } else {
            handleError(errors[0]);
          }
        });
      } catch (err) {
        console.log(err);
      }
    });

    this.setState({ loading: false });
  };

When I check my console log I see that the state has not updated and loading is still false.

What am I missing here?

This is happening, because you are calling this.setState({ loading: false }); in the end of your function.

You are setting loading: true and loading: false at the same time. When you your callback is invoking, the last setState function is also changed state value to false.

You should set loading state to false after request was received:

submitSecurityAnswer = () => {
  const { submit, handleError, navigate } = this.props;
  const { answer } = this.state;

  this.setState({ loading: true }, () => {
    console.log('setState', this.state)
    try {
      submit({ answer, ...this.props }).then(({ errors, status }) => {
        this.setState({ answer: '' });
        if (status && status === 200) {
          navigate();
        } else if (status === 401) {
          this.setState({ showError: true, loading: false });
        } else {
          handleError(errors[0]);
        }
      });
    } catch (err) {
      console.log(err);
    }
  });
};

That because you are setting loading to false before the async operation has completed.
Just move this.setState({ loading: false }); inside the callback ;

Edit
As a followup to your comment:

I tried removing that but saw the same results

I didn't say to remove it just move it inside the callback of setstate .
What happens is that you are setting it to true then false at the same call stack iteration and not waiting for the async operation to complete.

Consider this scenario which is similar to your code :
In this example i set the loading back to false before the async operation ( setTimeout ) is finished, hence i'm just overriding the previous state without waiting.

 class App extends React.Component { constructor(props) { super(props); this.state = { loading: false, message: '' }; } componentDidMount() { this.setState({ loading: true }, () => { console.log(this.state); setTimeout(() => { this.setState({ message: 'we have data' }); console.log(this.state); }, 500); }); this.setState({ loading: false }); } render() { const {message, loading} = this.state; return ( <div> {loading ? <div>Loading...</div> : <div>{message}</div>} </div> ); } } ReactDOM.render(<App />, document.getElementById("root")); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div> 

The fix will be to move the next state change inside the callback:
In this example i moved the line this.setState({ loading: false }); inside the callback after the async operation has completed and this fixed the unwanted behavior.

 class App extends React.Component { constructor(props) { super(props); this.state = { loading: false, message: '' }; } componentDidMount() { this.setState({ loading: true }, () => { console.log(this.state); setTimeout(() => { this.setState({ message: 'we have data' }); this.setState({ loading: false }); }, 500); }); } render() { const {message, loading} = this.state; return ( <div> {loading ? <div>loading...</div> : <div>{message}</div>} </div> ); } } ReactDOM.render(<App />, document.getElementById("root")); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM