簡體   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