0

Using React.

I an app that takes data that has been submited by a search bar and returns a list of ids. I want to take those ids and pass it through another api. However, I'm not sure how to do this. I've looked at a few other pages on stackoverflow like this and this. The first one is a little too vague and the second one has independent api calls that run in sequence.

Here's my code:

import React from 'react'
import api1 from '../api/api1'

import SearchBar from './SearchBar'

class App extends React.Component {
    state = { cards: [],  new_cards: []}; 

    onSearchSubmit = async (term) => {
    const response = await api1.get("api/decks/" + term)
    this.setState({ cards: response.data.data.cards, })
    } 

  render () {
    return (<div className='ui container'>
      <SearchBar onSubmit={this.onSearchSubmit} />
      {/* <CardList cards={this.state.cards} /> */}
    </div>)
  }
}
export default App

My code will take the first api and pass in the output ( a list of 36 items) down to the component CardList. However, the list isn't complete it needs to be a list of lists (you can tell I came over from python). I need to first make a call to a different api (one that is still local on my machine), grab the rest of the data and then pass in a new list (of 36 lists) to the component CardList. The second api works just fine by itself. I'm having trouble combining the two.

These are my thoughts, but it doesn't work. 1) create a new function getCardStats 2) use the map function to iterate over list one and run the api call. 3) append output of step 2) to a new list 4) return new list 5) pass in new list to CardList

There are 36 items in the first list and so the next api needs to be called 36 times. I don't know what the ramifications are of doing that many/little. Do we need to put something that waits for all 36 to finish or can we do them in parallel?

import React from 'react'
import api1 from '../api/api1'
import api2 from '../api/api2'

import SearchBar from './SearchBar'

class App extends React.Component {
    state = { cards: [] }; 

    onSearchSubmit = async (term) => {
    const response = await api1.get("api/decks/" + term)

    this.setState({ cards: response.data.. })
    this.getCardStats(this.state.cards)
    }        

    getCardStats = async (cards)=> {
      console.log('we are here')
      console.log(cards)
      const response = props.cards.map(card => {
          return await api2.get("api/cards/" + card)
      })
    }
    console.log('finished')
    console.log(response)
    this.setState({ new_cards: response.data.data.cards })

  render () {
    return (<div className='ui container'>
      <SearchBar onSubmit={this.onSearchSubmit} />
      {/* <CardList cards={this.state.new_cards} /> */}
    </div>)
  }
}
export default App

Thoughts?

Ram Budha
  • 180
  • 1
  • 11
Micah Pearce
  • 1,805
  • 3
  • 28
  • 61
  • That's already an obscene amount of calls... And what happens when the 36 becomes 360...? – SakoBu Dec 31 '18 at 22:34
  • So, I'm in charge of the 36 (more or less) and so I know it won't ever go more than that or less than that. And I'm looking at a way to make it one call, but I'm also curious to know how to do this (say if instead of 36 it was 2). – Micah Pearce Dec 31 '18 at 23:01

1 Answers1

2

Axios calls returns Promises, and I assume your API functions use it. Seems you're stuck on how to work with them, i.e. chain them together, or fire off a bunch and wait for them all to resolve. Hopefully I can explain how to do this without the intermediate step of saving state.

import React from 'react'
import api1 from '../api/api1'
import api2 from '../api/api2'

import SearchBar from './SearchBar'

class App extends React.Component {
  state = { cards: null }; 

  onSearchSubmit = term => {
    api1.get("api/decks/" + term) // returns a resolved promise, which is 'then'-able
      .then(cards => {
        // now we can setup our next API call
        // create an array of them and use Promise.all()

        const cardRequests = cards.map(card => api2.get(`api/cards/${card}`));
        return Promise.all(cardRequests); // This Promise resolves when all individual promise resolve, or rejects if any single one rejects.
      })
      .then(allCardsResults => {
        // Here we now have an array of resolved API values
        // Map, flatten, reduce, etc.. to whatever we need 
        // in state for the CardList

        const reducedCards = allCardsResults.reduce((acc, cardResult) => acc.concat(cardResult), []);

        this.setState({
          cards: reducedCards,
        });
      })
      .catch(error => {
        // do any clean up or state management here should
        // any errors or promises get rejected
      });
  }        

  render () {
    const { cards } = this.state;
    return (
      <div className='ui container'>
        <SearchBar onSubmit={this.onSearchSubmit} />
        { cards && <CardList cards={cards} />} // Only render if `cards` is truthy
      </div>
    )
  }
}
export default App

Working example

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];

new Promise((resolve, reject) => resolve(numbers))
.then(result => {
  console.log(`Initial numbers array: ${result}`);
  const promiseArray = result.map(key => {
    return Promise.resolve({ key, numbers });
  });
  return Promise.all(promiseArray);
})
.then(moreNumbers => {
  console.log(`More numbers: ${JSON.stringify(moreNumbers)}`);
  const reducedNumbers = moreNumbers.reduce((acc, mn) => acc.concat(mn.numbers), []);
  return Promise.resolve(reducedNumbers);
})
.then(finalNumberArray => console.log(`Final number array: ${finalNumberArray}`));
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • This is great. I'm going threw it and console.logging stuff so I can understand it. I'm getting this error `cards.map is not a function` – Micah Pearce Dec 31 '18 at 23:42
  • Thanks. Try logging the type `console.log(typeof cards);`, your first API may not be returning an array. If you really appreciated my answer could you accept it? – Drew Reese Dec 31 '18 at 23:53