簡體   English   中英

在React中使用async / await不正確地更新狀態

[英]Improperly updating state with async / await in React

我的目標是通過在componentDidMount中調用_addStock()函數,在componentDidMount狀態下的報價單中添加新的股票-包括報價,圖表,元信息。 但是,遍歷並調用_addStock ,每個數組中只有1只股票。

我可以通過在每個_addStock調用之間通過setTimeouts添加一個延遲來使此工作正常-但這_addStock了promises的目的。

我以為異步/等待,JavaScript將“同步”執行代碼。 但是,當我多次調用_addStock時,似乎只調用一次setState

這是怎么回事?

_fetchMeta_fetchQuote ,... etc返回承諾。

...

// this is what my state ends up looking like, only 1 per key when I'm expecting 4.
this.state = {
    stocks: [
        {
            symbol: 'MSFT',
            display: '...',
            color: '...'
        }
    ],
    metas: [
        {
            symbol: 'MSFT',
            name: '...',
            description: '...'
        }
    ],
    quotes: [
        {
            symbol: 'MSFT',
            price: '...',
            change: '...'
        }
    ],
    charts: [
        {
            symbol: 'MSFT',
            data: "..."
        }
    ]
}

//...

_addStock = async symbol => {
    const { metas, quotes, charts, stocks } = this.state;

    // check if stock already exists
    const hasStock = stocks.filter(s => s.symbol === symbol).length;
    if (hasStock) {
        return;
    }

    const meta = await this._fetchMeta(symbol);
    const quote = await this._fetchQuote(symbol);
    const chart = await this._fetchChart(symbol);
    const stock = {
        symbol: symbol.toUpperCase(),
        color: setStockColor(),
        display: true
    };

    this.setState({
        ...this.state,
        metas: [...metas, meta],
        quotes: [...quotes, quote],
        charts: [...charts, chart],
        stocks: [...stocks, stock]
    });
};

//...

componentDidMount() {
    const initStocks = ["FB", "MSFT", "NVDA", "AAPL"];
    initStocks.forEach(s => this._addStock(s));
}

用於使用Promise.all並行獲取多個URL。 在您的情況下,大致的實現是:

_addStock = async symbol => {
    ...
    let fetchMeta = this._fetchMeta.resolve(symbol);
    let fetchQuote = this._fetchQuote.resolve(symbol);
    let fetchMeta = this._fetchChart.resolve(symbol);

    let data = await Promise.all([
      fetchMeta,
      fetchQuote,
      fetchChart,
    ]).then((data) => {
      console.log(data); 
      // Inspect your data here setState accordingly        
      this.setState({ ... });    
    });        
};

多次調用_addStock多次調用setState導致多個setState操作並行運行,而無需等待先前setState更新的確認。 作為setState異步操作,您會得到這種怪異的行為。

為了解決這個問題,你可以添加一個多層次async/awaitpromise

_addStock = async symbol => {

    return new Promise((resolve,reject)){

        this.setState({
            ...this.state,
            metas: [...metas, meta],
            quotes: [...quotes, quote],
            charts: [...charts, chart],
            stocks: [...stocks, stock]
        },()=>{

            resolve(true); //give confirmation setState is updates successfully.
        });
    }
};

調用_addStock await

componentDidMount() {
    const initStocks = ["FB", "MSFT", "NVDA", "AAPL"];
    initStocks.forEach( async s => {
            await this._addStock(s)
    });
}

當您在短間隔內多次調用setState時,應以批處理方式響應所有setState的性能調用並集中執行它們,這就是為什么當第二次調用setState第一個請求仍未處理且組件state為空時,最后只插入一項的原因。

您可以使用以下方法異步引入數據,並且只會用最終數據更新狀態一次。 由於它不在每次迭代中使用狀態,因此它也將保持數據一致性。

_addStock = symbols => {
  const { metas, quotes, charts, stocks } = this.state;
  // a variable to keep track how many symbols have been processed
  let count = 0;

  symbols.forEach(async symbol => {
    // check if stock already exists
    const hasStock = stocks.some(s => s.symbol === symbol);
    if (!hasStock) {
      //make all api requests in parallel
      let meta = this._fetchMeta(symbol);
      let quote = this._fetchQuote(symbol);
      let chart = this._fetchChart(symbol);
      let stock = {
        symbol: symbol.toUpperCase(),
        color: setStockColor(),
        display: true
      };

      //wait for responses
      meta = await meta;
      quote = await quote;
      chart = await chart;

      //add all values to old state
      metas.push(meta);
      quotes.push(quote);
      chart.push(chart);
      stock.push(stock);
    }

    //update count
    ++count;

    //if all values have been retrieved update state
    if(count === symbols.length){
      this.setState({
        ...this.state,
        metas: [...metas],
        quotes: [...quotes],
        charts: [...charts],
        stocks: [...stocks]
      });
    }
  });
};

componentDidMount() {
    const initStocks = ["FB", "MSFT", "NVDA", "AAPL"];
    //Pass whole array to _addStock
    this._addStock(initStocks);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM