简体   繁体   English

在 componentDidMount() 中使用 React.setState 来获取嵌套 promise 中返回的数据?

[英]Using React.setState in componentDidMount() for data returned within nested promises?

I'm trying to put some data into state in a React app.我正在尝试将一些数据放入 React 应用程序中的状态。 The flow involves fetching a list of IDs from the HackerNews API, then taking each ID and making an additional API call to fetch the item associated with each ID.该流程涉及从 HackerNews API 获取 ID 列表,然后获取每个 ID 并进行额外的 API 调用以获取与每个 ID 关联的项目。 I ultimately want to have an array of 50 items in my component state (the resulting value of the each '2nd-level' fetch.我最终希望在我的组件状态中有一个包含 50 个项目的数组(每个“第二级”获取的结果值。

When I setState from JUST the single 'top-level' promise/API call, it works fine and my state is set with an array of IDs.当我从单个“顶级”promise/API 调用 setState 时,它​​工作正常,并且我的状态设置为一组 ID。 When I include a second .then() API call and try to map over a series of subsequent API calls, my state gets set with unresolved Promises, then the fetch() calls are made.当我包含第二个.then() API 调用并尝试映射一系列后续 API 调用时,我的状态设置为未解析的 Promise,然后进行fetch()调用。

开发者工具截图

I'm sure this a problem with my poor grasp on building appropriate async methods.我确信这是一个问题,因为我对构建适当的异步方法的掌握不力。

Can someone help me figure out what I'm doing wrong, and what the best practice for this is??有人可以帮我弄清楚我做错了什么,最好的做法是什么?

My component:我的组件:

import React from 'react'
import { fetchStoryList } from '../utils/api'

export default class Stories extends React.Component {
  state = {
    storyType: 'top',
    storyList: null,
    error: null,
  }

  componentDidMount () {
    let { storyType } = this.state

    fetchStoryList(storyType)
      .then((data) => {
        console.log("data", data)
        this.setState({ storyList: data })
      })
      .catch((error) => {
        console.warn('Error fetching stories: ', error)

        this.setState({
          error: `There was an error fetching the stories.`
        })
      })
  }

  render() {
    return (
      <pre>{JSON.stringify(this.state.storyList)}</pre>
    )
  }
}

My API Interface:我的API接口:

// HackerNews API Interface

function fetchStoryIds (type = 'top') {
  const endpoint = `https://hacker-news.firebaseio.com/v0/${type}stories.json`

  return fetch(endpoint)
    .then((res) => res.json())
    .then((storyIds) => {
      if(storyIds === null) {
        throw new Error(`Cannot fetch ${type} story IDs`)
      }

      return storyIds
    })
}

function fetchItemById(id) {
  const endpoint = `https://hacker-news.firebaseio.com/v0/item/${id}.json`

  return fetch(endpoint)
    .then((res) => res.json())
    .then((item) => item)
}

export function fetchStoryList (type) {
  return fetchStoryIds(type)
    .then((idList) => idList.slice(0,50))
    .then((idList) => {
      return idList.map((id) => {
        return fetchItemById(id)
      })
    })
//ABOVE CODE WORKS WHEN I COMMENT OUT THE SECOND THEN STATEMENT

One solution would be to update fetchStoryList() so that the final .then() returns a promise that is resolved after all promises in the mapped array (ie from idList.map(..) ) are resolved.一种解决方案是更新fetchStoryList()以便最终的.then()返回一个承诺,该承诺在映射数组(即来自idList.map(..) )中的所有承诺都得到解决后得到解决。

This can be achieved with Promise.all() .这可以通过Promise.all()来实现。 Promise.all() take an array as an input, and will complete after all promises in the supplied array have successfully completed: Promise.all()将一个数组作为输入,并在提供的数组中的所有 promise 成功完成后完成:

export function fetchStoryList(type) {

  return fetchStoryIds(type)
    .then((idList) => idList.slice(0,50))
    .then((idList) => {

      /* Pass array of promises from map to Promise.all() */
      return Promise.all(idList.map((id) => {
        return fetchItemById(id)
      });

    });
}

You are not waiting for some asynchronous code to "finish"你不是在等待一些异步代码“完成”

ie IE

.then((idList) => {
  return idList.map((id) => {
    return fetchItemById(id)
  })
})

returns returns an array of promises that you are not waiting for Returns 返回一系列你没有等待的承诺

To fix, use Promise.all要修复,请使用 Promise.all

(also cleaned up code removing redundancies) (还清理了去除冗余的代码)

function fetchStoryIds (type = 'top') {
    const endpoint = `https://hacker-news.firebaseio.com/v0/${type}stories.json`;

    return fetch(endpoint)
    .then((res) => res.json())
    .then((storyIds) => {
        if(storyIds === null) {
            throw new Error(`Cannot fetch ${type} story IDs`);
        }
        return storyIds;
    });
}

function fetchItemById(id) {
    const endpoint = `https://hacker-news.firebaseio.com/v0/item/${id}.json`
    return fetch(endpoint)
    .then(res => res.json());
}

export function fetchStoryList (type) {
    return fetchStoryIds(type)
    .then(idList => Promise.all(idList.slice(0,50).map(id => fetchItemById(id)));
}

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

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