【问题标题】:React fetch all data from api at startupReact 在启动时从 api 获取所有数据
【发布时间】:2019-04-15 21:27:09
【问题描述】:

首先,我想让你知道我刚刚开始使用 React 和 Context API。

这是我的情况:我想在应用程序启动时从PunkAPI 获取所有啤酒(在获取时显示加载器)并将所有这些啤酒放在上下文中以访问子组件。

我的问题是 API 没有给出所有啤酒页面的总数,所以我必须检查结果的长度并在长度等于 0 时停止获取,然后将数据置于状态。

然后,加载完所有内容后,我想为所有啤酒添加一个属性(favorite)并首先将其设置为 false,然后,如果用户已登录,则检索他的收藏夹,并在收藏夹列表所关注的上下文中为每种啤酒设置为 true。

这是我现在带来的:

import React from 'react'
import {jsonFetch} from 'easyfetch'
import _ from 'lodash'
import {UserAuthContext} from './userContext'
import {db} from '../firebase'

const defaultBeersContext = {
  beers: null,
  isFetchingBeers: true,
  errorFetchingBeers: null,
}

export const BeersContext = React.createContext(defaultBeersContext)

export default class BeersProvider extends React.Component {
  constructor(props) {
    super(props)
    this.state = defaultBeersContext
  }

  async componentDidMount() {
    let allBeers = await this.fetchAllBeers(1, [])
    allBeers.forEach(beer => (beer.favorite = false))
    console.log(allBeers.length)
    console.log(allBeers)
    const {isUserSignedIn, user} = this.context
    if (isUserSignedIn && user) {
      db.collection('users')
        .doc(user.uid)
        .get()
        .then(doc => {
          const favoriteBeers = doc.data().favorites
          favoriteBeers.forEach(elem => {
            const index = allBeers.findIndex(beer => beer.id === elem)
            allBeers[index].favorite = true
          })
        })
    }
    this.setState({beers: allBeers, isFetchingBeers: false})
  }

  fetchAllBeers(page, beers) {
    jsonFetch(`https://api.punkapi.com/v2/beers?page=${page}&per_page=80`)
      .get()
      .then(data => {
        if (data && data.length > 0) {
          this.fetchAllBeers(page + 1, _.concat(beers, data))
        } else {
          return beers
        }
      })
      .catch(error => this.setState({errorFetchingBeers: error, isFetchingBeers: false}))
  }

  render() {
    const {children} = this.props
    const {beers, isFetchingBeers, errorFetchingBeers} = this.state
    return (
      <BeersContext.Provider value={{beers, isFetchingBeers, errorFetchingBeers}}>{children}</BeersContext.Provider>
    )
  }
}

BeersProvider.contextType = UserAuthContext

P.S:我使用的 jsonFetch 来自这里:easyfetch

但这不起作用,说:

 Unhandled Rejection (TypeError): Cannot read property 'forEach' of null

  21 | async componentDidMount() {
  22 |   this.fetchAllBeers(1, []).then(result => this.setState({beers: result}))
> 23 |   this.state.beers.forEach(beer => (beer.favorite = false))
     | ^  24 |   const {isUserSignedIn, user} = this.context
  25 |   if (isUserSignedIn && user) {
  26 |     db.collection('users')

【问题讨论】:

    标签: javascript reactjs recursion fetch-api


    【解决方案1】:

    在错误中指出您正在尝试遍历 defaultBeersContext 中的空 beers = null 数组。

    我认为您的意图是从 API 加载啤酒数据以填充啤酒的状态,然后通过填充的啤酒数组进行 forEach。

    您收到错误是因为以下行是异步的,并且在您尝试访问其返回的啤酒数据之前未完成:

    this.fetchAllBeers(1, []).then(result => this.setState({beers: result}))
    

    因此在下一行

    this.state.beers.forEach(...)
    

    该状态仍然包含旧的默认啤酒默认数据(啤酒:null),因为您的上一行尚未完成对啤酒数据的提取。

    如果您使用 Promise,那么您需要等到获取新的 beers 数据后再运行您的 forEach,如下所示:

    this.fetchAllBeers(1, [])
        .then(result => {
            this.setState({beers: result});
            allBeers.forEach(beer => (beer.favorite = false));
            ...
            })
    

    您想要这样做是因为您的以下几行取决于获取啤酒数据的完成情况。

    【讨论】:

      【解决方案2】:

      感谢@Shawn Andrews 的帮助,我现在更了解 Promises 背后的过程。我最终决定将我的收藏夹和啤酒的合并移动到一个儿童容器组件中,让 beersContext 获取所有内容,而 userContext 获取收藏夹。然后,容器组件将两者取并合并,然后将修改后的值传递给渲染组件!

      这是我拥有的最后一个 BeersContext:

      import React from 'react'
      import {jsonFetch} from 'easyfetch'
      import _ from 'lodash'
      import {UserAuthContext} from './UserContext'
      
      const defaultBeersContext = {
        beers: null,
        isFetchingBeers: true,
        errorFetchingBeers: null,
      }
      
      export const BeersContext = React.createContext(defaultBeersContext)
      
      export default class BeersProvider extends React.Component {
        constructor(props) {
          super(props)
          this.state = defaultBeersContext
        }
      
        componentDidMount() {
          this.fetchAllBeers(1, [])
        }
      
        async fetchAllBeers(page, beers) {
          await jsonFetch(`https://api.punkapi.com/v2/beers?page=${page}&per_page=80`)
            .get()
            .then(data => {
              if (data && data.length > 0) {
                data.forEach(item => (item.favorite = false))
                this.fetchAllBeers(page + 1, _.concat(beers, data))
              } else {
                this.setState({beers, isFetchingBeers: false})
              }
            })
            .catch(error => this.setState({errorFetchingBeers: error, isFetchingBeers: false}))
        }
      
        render() {
          const {children} = this.props
          const {beers, isFetchingBeers, errorFetchingBeers} = this.state
          return (
            <BeersContext.Provider value={{beers, isFetchingBeers, errorFetchingBeers, fetchAllBeers: this.fetchAllBeers}}>
              {children}
            </BeersContext.Provider>
          )
        }
      }
      
      BeersProvider.contextType = UserAuthContext
      

      【讨论】:

        猜你喜欢
        • 2021-01-21
        • 1970-01-01
        • 2021-12-10
        • 2021-12-16
        • 1970-01-01
        • 2021-09-27
        • 2020-03-30
        • 1970-01-01
        • 2021-12-23
        相关资源
        最近更新 更多