繁体   English   中英

ReactJS:异步API数据提取完成后,我无法获得使用API​​数据更新的状态

[英]ReactJS: I can not get the state to update with API data when the asynchronous API data fetch is completed

在将状态设置为来自返回的异步API请求的数据之前,渲染组件时遇到了一些问题。 我有一个fetch()方法,该方法会触发,从API返回数据,然后将状态设置为该数据。 这是处理此问题的代码块:

class App extends Component {
  constructor() {
    super();
    this.state = {
      currentPrice: null,
    };
  }
    componentDidMount() {
        const getCurrentPrice = () => {
          const url = 'https://api.coindesk.com/v1/bpi/currentprice.json';

          fetch(url).then(data => data.json())
          .then(currentPrice => {
            this.setState = ({
              currentPrice: currentPrice.bpi.USD.rate
            })
            console.log('API CALL', currentPrice.bpi.USD.rate);
          }).catch((error) => {
            console.log(error);
          })
        }
       getCurrentPrice();
    } 

您会注意到我用来检查API数据是否正在返回的console.log('API CALL', currentPrice.bpi.USD.rate ),并且绝对是。 currentPrice.bpi.USD.rate在控制台中按预期返回一个整数(例如2345.55 )。

太好了,因此我假设this.setState = ({ currentPrice: currentPrice.bpi.USD.rate })可以正确设置状态,因为已成功接收到此数据。

现在,我将组件渲染如下:

render() {
    return (
      <div>
        <NavigationBar />
        <PriceOverview data={this.state.currentPrice}/>
      </div>
    );
  }
}
export default App;

有了这个,我期望能够像下面这样在PriceOverview.js组件中访问此数据: this.props.data

我已经使用console.log()来检查this.props.data组件中的PriceOverview.js ,并且我得到了“ null”,因为这是我PriceOverview.js设置的默认值。 我遇到的问题是,组件在API提取运行之前就渲染了,并使用返回的数据更新了状态。 因此,当App.js渲染PriceOverview.js组件时,它仅将currentPrice: null传递给它,因为异步fetch()尚未在渲染之前返回数据。

我的困惑在于this.setState 我已经读过, this.setState被调用,React就会调用render。 因此,在我看来,一旦fetch()请求返回,它将调用this.setState并将状态更改为返回的数据。 反过来,这将导致重新渲染,并且新的状态数据应该可用。 如果我不说我在这里感到困惑,那我会撒谎。 我假设一旦fetch()返回,它将使用请求的数据更新状态,然后触发重新渲染。

这里肯定有我想念的东西,但是我的经验不足让我一个人..冷..在绝望的黑暗中。 我在使用“硬编码”数据时没有问题,因为我可以很好地传递数据而不必担心数据何时返回。 例如,如果我将App.js中的状态设置为this.state = { currentPrice: [254.55] } ,则可以通过零问题通过this.props.dataPriceOverview.js访问它。 是异步API请求将我带到了这里,恐怕今晚它已成为我的最佳选择。

此处完整显示了App.js:

import React, { Component } from 'react';
import './components/css/App.css';
import NavigationBar from './components/NavigationBar';
import PriceOverview from './components/PriceOverview';

class App extends Component {
  constructor() {
    super();
    this.state = {
      currentPrice: null,
    };
  }
  componentDidMount() {
    const getCurrentPrice = () => {
      const url = 'https://api.coindesk.com/v1/bpi/currentprice.json';

      fetch(url).then(data => data.json())
      .then(currentPrice => {
        this.setState = ({
          currentPrice: currentPrice.bpi.USD.rate
        })
        console.log('API CALL', currentPrice.bpi);
      }).catch((error) => {
        console.log(error);
      })
    }
    getCurrentPrice();
  }

render() {
    return (
      <div>
        <NavigationBar />
        <PriceOverview data={this.state.currentPrice}/>
      </div>
    );
  }
}
export default App;

这是完整的PriceOverview.js:

import React, { Component } from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';

class PriceOverview extends Component {

    constructor(props) {
        super(props);
        this.state = {
            currentPrice: this.props.data
        }
    }

    render() {
     return (
          <div className="overviewBar">
            <div className="currentPrice panel">
                 { this.state.currentPrice != null ? <div className="price">{this.state.currentPrice}</div> : <div className="price">Loading...</div> }
            </div>
          </div>
    )
  }
}
export default PriceOverview;

预先感谢您的任何帮助,非常感谢。

问题是任何JS类的构造函数都只能调用一次。 每当您调用this.setState时,就会调用render方法。

因此,基本上,您是在构造函数中一次将currentPrice设置为null ,然后使用state访问它,因此它将始终为null。

更好的方法是使用道具。

您可以在PriceOverview.js执行类似的PriceOverview.js

import React, { Component } from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';

class PriceOverview extends Component {

constructor(props) {
    super(props);
    this.state = {

    }
}

render() {
 return (
      <div className="overviewBar">
        <div className="currentPrice panel">
             { this.props.data!= null ? <div className="price">{this.props.data}</div> : <div className="price">Loading...</div> }
        </div>
      </div>
    )
  }
}
export default PriceOverview;  

或者,您可以使用PriceOverview.js生命周期方法componentWillReceiveProps来更新PriceOverview.js的状态

componentWillReceiveProps(nextProps) {
    this.setState({
        currentPrice:nextProps.data
    });
}  

render() {
 return (
      <div className="overviewBar">
        <div className="currentPrice panel">
         { this.state.currentPrice != null ? <div className="price">{this.state.currentPrice }</div> : <div className="price">Loading...</div> }
        </div>
      </div>
    )
  }
}

好的,第一件事,当您在React上编写代码时,保持状态的组件是类的基础组件,所以...我在这里看到的是您正在创建两个类的基础组件,因此当您从应用程序类传递props时到PriceOverview的组件是另一个基本类组件,您实际上什么都不做...因为当您在PriceOverview上的构造函数被调用时,您正在该Component和先前的状态上创建一个新状态(这就是您想要的状态)传递)被覆盖,这就是为什么当您要显示它时您似乎为空。 因此,只要将PriveOverview组件更改为基于函数的组件(或哑组件),它就可以工作。 这样,当您通过道具传递状态时,就可以在div中显示正确的状态。 这是什么样子。

import React from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';

const PriceOverview = (data) => {
  return (
      <div className="overviewBar">
        <div className="currentPrice panel">
        //Im calling data here because that's the name you gave it as ref
        //No need to use 'this.props' you only use that to pass down props
          {data != null ? <div className="price"> 
          {data}</div> : <div className="price">Loading...</div> 
         }
        </div>
      </div>
            )
        }
   }
export default PriceOverview;

每当您编写新组件时,如果您的组件只是在其中返回标记,则始终从函数基础组件开始,并且您需要将一些数据传递给其父组件以对其进行更新(在其中进行api调用或在其中设置状态)并传递按下要通过ref渲染的道具。 尽可能多地阅读React文档,希望这个解释有用(如果您不太了解我的道歉,因为我的语法我必须努力解决)

 this.setState ({
          currentPrice: currentPrice.bpi.USD.rate
        })

不要在this.setStatethis.setState =

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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