简体   繁体   English

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

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

I am having a bit of an issue rendering components before the state is set to the data from a returned asynchronous API request. 在将状态设置为来自返回的异步API请求的数据之前,渲染组件时遇到了一些问题。 I have a fetch() method that fires off, returns data from an API, and then sets the state to this data. 我有一个fetch()方法,该方法会触发,从API返回数据,然后将状态设置为该数据。 Here is that block of code that handles this: 这是处理此问题的代码块:

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

You will notice the console.log('API CALL', currentPrice.bpi.USD.rate ) that I use to check if the API data is being returned, and it absolutely is. 您会注意到我用来检查API数据是否正在返回的console.log('API CALL', currentPrice.bpi.USD.rate ),并且绝对是。 currentPrice.bpi.USD.rate returns an integer ( 2345.55 for example) right in the console as expected. currentPrice.bpi.USD.rate在控制台中按预期返回一个整数(例如2345.55 )。

Great, so then I assumed that this.setState = ({ currentPrice: currentPrice.bpi.USD.rate }) should set the state without an issue, since this data was received back successfully. 太好了,因此我假设this.setState = ({ currentPrice: currentPrice.bpi.USD.rate })可以正确设置状态,因为已成功接收到此数据。

So I now render the components like so: 现在,我将组件渲染如下:

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

With this, I was expecting to be able to access this data in my PriceOverview.js component like so: this.props.data 有了这个,我期望能够像下面这样在PriceOverview.js组件中访问此数据: this.props.data

I have used console.log() to check this.props.data inside my PriceOverview.js component, and I am getting 'null' back as that is the default I set intially. 我已经使用console.log()来检查this.props.data组件中的PriceOverview.js ,并且我得到了“ null”,因为这是我PriceOverview.js设置的默认值。 The issue I am having is that the components render before the API fetch has ran it's course and updated the state with the returned data. 我遇到的问题是,组件在API提取运行之前就渲染了,并使用返回的数据更新了状态。 So when App.js renders the PriceOverview.js component, it only passes currentPrice: null to it, because the asynchronous fetch() has not returned the data prior to rendering. 因此,当App.js渲染PriceOverview.js组件时,它仅将currentPrice: null传递给它,因为异步fetch()尚未在渲染之前返回数据。

My confusion lies with this.setState . 我的困惑在于this.setState I have read that React will call render any time this.setState is called. 我已经读过, this.setState被调用,React就会调用render。 So in my mind, once the fetch() request comes back, it calls this.setState and changes the state to the returned data. 因此,在我看来,一旦fetch()请求返回,它将调用this.setState并将状态更改为返回的数据。 This in turn should cause a re-render and the new state data should be available. 反过来,这将导致重新渲染,并且新的状态数据应该可用。 I would be lying if I didn't say I was confused here. 如果我不说我在这里感到困惑,那我会撒谎。 I was assuming that once the fetch() returned, it would update the state with the requested data, and then that would trigger a re-render. 我假设一旦fetch()返回,它将使用请求的数据更新状态,然后触发重新渲染。

There has to be something obvious that I am missing here, but my inexperience leaves me alone.. cold.. in the dark throws of despair. 这里肯定有我想念的东西,但是我的经验不足让我一个人..冷..在绝望的黑暗中。 I don't have an issue working with 'hard coded' data, as I can pass that around just fine without worry of when it returns. 我在使用“硬编码”数据时没有问题,因为我可以很好地传递数据而不必担心数据何时返回。 For example, if I set the state in App.js to this.state = { currentPrice: [254.55] } , then I can access it in PriceOverview.js via this.props.data with zero issue. 例如,如果我将App.js中的状态设置为this.state = { currentPrice: [254.55] } ,则可以通过零问题通过this.props.dataPriceOverview.js访问它。 It's the async API request that is getting me here, and I am afraid it has gotten the best of me tonight. 是异步API请求将我带到了这里,恐怕今晚它已成为我的最佳选择。

Here App.js in full: 此处完整显示了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;

Here is PriceOverview.js in full: 这是完整的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;

Thank you in advance to any help, it's much appreciated. 预先感谢您的任何帮助,非常感谢。

The thing is constructor of any JS class is called only once. 问题是任何JS类的构造函数都只能调用一次。 It is the render method that is called whenever you call this.setState . 每当您调用this.setState时,就会调用render方法。

So basically you are setting currentPrice to null for once and all in constructor and then accessing it using state so it will always be null. 因此,基本上,您是在构造函数中一次将currentPrice设置为null ,然后使用state访问它,因此它将始终为null。

Better approch would be using props. 更好的方法是使用道具。

You can do something like this in your PriceOverview.js . 您可以在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;  

Or you can use react lifecycle method componentWillReceiveProps to update the state of PriceOverview.js 或者,您可以使用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>
    )
  }
}

Ok First thing, when you're writting code on React the components that hold state are the class base components so ... What I see here is that you're creating two class base components so when you pass down props from your app class component to your PriceOverview wich is another class base component you're essentially doing nothing... Because when your constructor on your PriceOverview get call you're creating a new state on that Component and the previous state ( that's is the one you want to pass down) is being overwritten and that's why you're seem null when you want to display it. 好的,第一件事,当您在React上编写代码时,保持状态的组件是类的基础组件,所以...我在这里看到的是您正在创建两个类的基础组件,因此当您从应用程序类传递props时到PriceOverview的组件是另一个基本类组件,您实际上什么都不做...因为当您在PriceOverview上的构造函数被调用时,您正在该Component和先前的状态上创建一个新状态(这就是您想要的状态)传递)被覆盖,这就是为什么当您要显示它时您似乎为空。 So it should work if you just change your PriveOverview component to a function base component ( or a dumb component). 因此,只要将PriveOverview组件更改为基于函数的组件(或哑组件),它就可以工作。 So this way when you pass down the state via props, you're displaying the correct state inside of your div. 这样,当您通过道具传递状态时,就可以在div中显示正确的状态。 This is how would look like. 这是什么样子。

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;

Whenever you're writing new components start always with function base components if you component is just returning markup in it and you need to pass some data go to his parent component update it (making the api calls there or setting the state there) and pass down the props you want to render via ref. 每当您编写新组件时,如果您的组件只是在其中返回标记,则始终从函数基础组件开始,并且您需要将一些数据传递给其父组件以对其进行更新(在其中进行api调用或在其中设置状态)并传递按下要通过ref渲染的道具。 Read the React docs as much as you can, hope this explanation was useful (my apologies in advance if you don't understand quite well 'cause of my grammar I've to work on that) 尽可能多地阅读React文档,希望这个解释有用(如果您不太了解我的道歉,因为我的语法我必须努力解决)

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

Do not put an = in this.setState 不要在this.setStatethis.setState =

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

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