setState 回调未按预期工作

setState callback not working as expected

我有一个正在运行的应用程序,我只是想在我的应用程序调用提交时将状态设置为 loading: true,这样我就可以显示加载屏幕。因为我想确保在 进行加载调用之前 设置状态,所以我使用了回调。但是我没有看到我使用以下代码进行加载更新:

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 });
  };

当我检查我的控制台日志时,我发现状态没有更新,加载仍然是错误的。

我在这里错过了什么?

那是因为您在异步操作完成之前将 loading 设置为 false
只需将 this.setState({ loading: false }); 移到 callback;

编辑
作为您评论的跟进:

I tried removing that but saw the same results

我没说要删除它只是 setstate 的回调中移动 它。
发生的情况是您在同一调用堆栈迭代中将其设置为 true 然后 false 而不是等待异步操作完成。

考虑与您的代码类似的场景:
在此示例中,我在异步操作 (setTimeout) 完成之前将 loading 设置回 false,因此我只是在不等待的情况下覆盖之前的状态。

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>

解决方法是将下一个状态更改移到回调中:
在这个例子中,我在异步操作完成后将 this.setState({ loading: false }); 行移到了回调中,这修复了不需要的行为。

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>

发生这种情况,因为您在函数末尾调用 this.setState({ loading: false });

您正在同时设置 loading: trueloading: false。当您调用回调时,最后一个 setState 函数也将状态值更改为 false。

您应该在收到请求后将加载状态设置为 false:

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);
    }
  });
};