【问题标题】:setState callback not working as expectedsetState 回调未按预期工作
【发布时间】:2017-10-11 18:13:48
【问题描述】:

我有一个正在运行的应用程序,只是在我的应用程序发出提交调用时尝试将状态设置为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 });
  };

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

我在这里错过了什么?

【问题讨论】:

  • 如果我理解您正在尝试正确执行的操作,您的 this.setState({loading:false}) 应该在 navigate(); 之前或之后
  • 实际上,如果您将this.setState({ answer: '' }); 更改为this.setState({ answer: '', loading:false });,它也应该可以工作
  • 是的,我正在尝试 this.setState({loading:false})submit() 之前
  • 然后放在submit()函数之前
  • 您同时拨打this.setState({ loading: true })this.setState({ loading: false })。后一种调用会覆盖第一种。

标签: javascript reactjs setstate


【解决方案1】:

发生这种情况,因为您在函数结束时调用了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);
    }
  });
};

【讨论】:

  • 这里又在setState的回调函数中调用了setState。我们可以这样做吗?我的意思是我们应该在 setState 的回调中写 setState 吗?
【解决方案2】:

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

编辑
作为您评论的后续行动:

我尝试删除它,但看到相同的结果

我没有说要删除它,只是在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>

【讨论】:

  • 我没有说删除,我说的是在回调函数中移动它。看我的例子
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-11
  • 1970-01-01
  • 1970-01-01
  • 2017-09-01
  • 2017-01-15
  • 1970-01-01
相关资源
最近更新 更多