React componentDidMount() 初始化状态

Posted

技术标签:

【中文标题】React componentDidMount() 初始化状态【英文标题】:React componentDidMount() initializing state 【发布时间】: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 的异步性质造成的。

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

【问题讨论】:

【参考方案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);
    )

【讨论】:

顺便说一句,我建议使用 React Chrome 扩展程序 (chrome.google.com/webstore/detail/react-developer-tools),它使您能够实时检查状态和道具,并使调试这样的事情变得更加容易。【参考方案4】:

您的代码存在一些问题:

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

【讨论】:

以上是关于React componentDidMount() 初始化状态的主要内容,如果未能解决你的问题,请参考以下文章

React componentDidMount

async/await 不适用于 ComponentDidMount 中的 react.js

text #react #componentDidMount

React componentDidMount“解析错误:缺少分号”

Apollo + React:数据未出现在 componentDidMount 生命周期中

React componentDidMount() 初始化状态