【问题标题】:React componentDidMount() initializing stateReact componentDidMount() 初始化状态
【发布时间】:2019-01-04 18:36:22
【问题描述】:

在 React 中处理一个小项目,我正在使用 Axios 从经过测试的 API 中获取信息。问题是当我拉入数据并尝试更新组件状态时,来自 API 的值没有填充到要在组件中呈现的状态。

我一直在尝试获取如下状态的数据:

  componentDidMount() {
    this.state.databases.forEach(d => {
      axios.get(`http://localhost:3204/customer/${d}/count`).then(value => {
        this.setState({ counts: this.state.counts.push(value.data) });
      });
    });
    console.log(this.state);
  }

以前的版本是这样写的:

componentDidMount() {
    let internalCounts = [];
    this.state.databases.forEach(d => {
        axios.get(`http://localhost:3204/customer/${d}/count`).then(value => {
            internalCounts.push(value.data);
        });
    });
    this.setState({count: internalCounts});
}

这两种情况最终都会得到相同的结果。状态永远不会用新的计数数组真正更新。我怀疑这是由于Axios setState 的异步性质造成的。

什么是让这两者停止相互竞争的简单有效的方法?

【问题讨论】:

    标签: javascript arrays reactjs setstate


    【解决方案1】:

    不要在设置状态后立即使用console.log,因为它是异步的。回调到setState

    this.setState({...}, () => console.log(...))
    

    或使用console.log 在您的渲染方法中检查您的状态

    另外,不要这样做forEach,如果适合您,请尝试使用Promise.all 一次性提出所有请求。

    componentDidMount() {
        const promiseArray = this.state.databases.map( d =>
          axios.get(`http://localhost:3204/${d}/count`)
        )
        Promise.all( promiseArray )
          .then( ( results ) => {
            const data = results.map( result => result.data);
            this.setState( prevState => ({
              counts: [ ...prevState.counts, ...data]
            }))
          })
      }
    

    【讨论】:

      【解决方案2】:

      问题是我试图改变状态。 (我对不可变数据感到很恐惧……所以我必须习惯一些事情。)

      我的解决方案代码如下:

      componentDidMount(){
          this.state.databases.forEach(d => {
              axios.get(`http://localhost:3204/${d}/count`).then(value => {
                  this.setState(prevState => ({
                      counts: [...prevState.counts, value.data]
                  }));
               });
          });
      }
      

      【讨论】:

      • 我更喜欢上面的方法,因为它只设置一次状态,因此您可以避免多次重新渲染。
      • 这是有道理的。但是,计数的数据结构发生了变化,我移至一个对象,并在对象键上进行迭代,以确保使用正确的标题显示正确的数据。 @ForrestLyman,介意我们在问题之外谈论这个吗?我有它的工作,但我想重构只更新一次状态。
      • 当然。您可以通过 forrestswork@gmail.com 或 LinkedIn 联系我。
      【解决方案3】:

      在第一个版本中,由于服务的异步特性,您是正确的。您正在记录输出,但这是在服务解决之前发生的。

      在第二个(上一个)示例中,您要在状态解析之前设置状态。我会推荐以下方法,创建一个 promise 数组,然后在更新状态之前等待它们全部解决:

      componentDidMount() {
          const promises = [];
          this.state.databases.forEach(d => {
              promises.push(axios.get(`http://localhost:3204/customer/${d}/count`));
          });
          Promise.all(promises).then(results => {
              const internalCounts = [];
              results.map(result => {
                  internalCounts.push(result.data);
              });
              this.setState({count: internalCounts});
          })
      }

      【讨论】:

      【解决方案4】:

      您的代码存在一些问题:

      1. Array.prototype.push(value.data) 返回新长度,而不是新数组。所以this.setState({ counts: this.state.counts.push(value.data) }); 正在将state.counts 变成一个数字值this.state.counts.length + 1,这可能是不希望的。
      2. 如果要打印出新的状态,console.log(this.state);应该放入setState()的回调参数中。
      3. 您之前的版本在setState 之前没有等待axios 调用。你的想法是对的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-08
        • 2018-08-22
        • 2021-07-09
        • 2019-09-02
        • 2023-03-08
        • 1970-01-01
        • 1970-01-01
        • 2021-03-21
        相关资源
        最近更新 更多