Реагировать на получение всех данных из API при запуске - PullRequest
0 голосов
/ 13 ноября 2018

Прежде всего, я хотел бы сообщить, что я только начинаю с React и Context API.

Вот моя ситуация: я хотел бы получить все пиво из PunkAPI при запуске приложения (отображение загрузчика во время выборки) и размещение всего этого пива в контексте для доступа к его подкомпонентам.

Моя проблема заключается в том, что API не даетобщее количество страниц пива, поэтому я должен проверить длину результатов и прекратить получать, когда длина равна 0, а затем поместить данные в состояние.

Затем, как только все будет загружено, я хотел быдобавить свойство ( favourite ) для всех сортов пива и сначала установить для него значение 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

PS: Я использую 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')

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

спасибо @Shawn Andrews за помощь, теперь я понимаю, что происходит за обещаниями.В конце концов я решил перенести слияние моих избранных и моего пива в дочерний контейнерный компонент, позволяя 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
0 голосов
/ 13 ноября 2018

В сообщении об ошибке вы пытаетесь перебрать пустой массив beers = null в defaultBeersContext.

Я думаю, что вы намеревались загрузить данные пива из API, чтобы заполнить состояние пива, а затем forEach через этот заполненный массив пива.

Вы получаете сообщение об ошибке, потому что следующая строка является асинхронной и не завершена, прежде чем вы попытаетесь получить доступ к ее возвращенным данным пива:

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

Поэтому на следующей строке

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

Состояние по-прежнему содержит данные по умолчанию для старых пива по умолчанию (beers: null), поскольку ваша предыдущая строка еще не закончила выборку данных пива.

Если вы используете обещания, вам следует подождать, пока вы не получите новые данные пива, прежде чем запускать свой forEach, например:

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

Вы хотите сделать это, потому что ваши следующие строки зависят от завершения извлечения данных пива.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...