0

I'm developing a project in React Native. The problem I have encountered is that the fetches all develop at the same time, instead I would like one fetch to wait before the end of another fetch to use its data.

I would then like to use idTitle in the second fetch, but I am unable because they are executed simultaneously

function SongScreen({ route }) {

  const { textArtist, textSong } = route.params;

  const geniusUrl = 'https://api.genius.com';

  const [isLoading, setLoading] = useState(true);

  const [idTitle, setIdTitle] = useState([]);
  const [idVideoYT, setIdVideoYT] = useState([]);
       
  useEffect(async () => {
    fetch(geniusUrl + '/search?q=' + textSong, {
      headers: { 'Authorization': 'Bearer ' + geniusToken }
    })
      .then((response) => response.json())
      .then((json) => {
        for (const value of json.response.hits) {
          if ((value.result.title.toLowerCase() == textSong.toLowerCase()) &&
            (value.result.primary_artist.name.toLowerCase() == textArtist.toLowerCase())) {
            setIdTitle(value.result.api_path)
            setIdArtist(value.result.primary_artist.api_path)
          }
        }
      })
      .catch((error) => { AlertView(), setModalVisible(true) })
      .finally(() => setLoading(false));
  }, []);

          
  useEffect(() => {
    fetch(geniusUrl + idTitle, {
      headers: { 'Authorization': 'Bearer ' + geniusToken }
    })
      .then((response) => response.json())
      .then((json) => {
        for (const value of json.response.song.media) {
          if (value.provider == 'youtube')
            setIdVideoYT(value.url)
        }
      })
      .catch((error) => alert(error)) //{ AlertView(), setModalVisible(true) })
      .finally(() => setLoading(false));
  }, []);
    
  return (
    //.....
  )
}

EDIT: I tried concatenating like this, but it keeps giving the same error:

    useEffect(async () => {
    fetch(geniusUrl + '/search?q=' + textSong, {
      headers: { 'Authorization': 'Bearer ' + geniusToken }
    })
      .then((response) => response.json())
      .then((data) => {
        for (const value of data.response.hits) {
          if ((value.result.title.toLowerCase() == textSong.toLowerCase()) &&
            (value.result.primary_artist.name.toLowerCase() == textArtist.toLowerCase())) {
            setIdTitle(value.result.api_path)   
            setIdArtist(value.result.primary_artist.api_path)   
          }
        }
      })
      .then(
        fetch(geniusUrl + idTitle, {
          headers: { 'Authorization': 'Bearer ' + geniusToken }
        })
          .then((response) => response.json())
          .then((json) => {
            for (const value of json.response.song.media) {
              if (value.provider == 'youtube')
                setIdVideoYT(value.url)
            }
          })
          .catch((error) => alert(error)) //{ AlertView(), setModalVisible(true) })
          .finally(() => setLoading(false))
      )
      .catch((error) => { AlertView(), setModalVisible(true) })
      .finally(() => setLoading(false));
  }, []);

EDIT 2: I tried adding the then inside the if, but it gives as error that it doesn't find the then variable:

useEffect(async () => {
fetch(geniusUrl + '/search?q=' + textSong, {
  headers: { 'Authorization': 'Bearer ' + geniusToken }
})
  .then((response) => response.json())
  .then((data) => {
    for (const value of data.response.hits) {
      if ((value.result.title.toLowerCase() == textSong.toLowerCase()) &&
        (value.result.primary_artist.name.toLowerCase() == textArtist.toLowerCase())) {
         setIdTitle(value.result.api_path)
         setIdArtist(value.result.primary_artist.api_path)

          then(() => fetch(geniusUrl + value.result.api_path, {
            headers: { 'Authorization': 'Bearer ' + geniusToken }
          })
            .then((response) => response.json())
            .then((json) => {
              for (const value of json.response.song.media) {
                if (value.provider == 'youtube')
                  setIdVideoYT(value.url)
              }
            })
            .catch((error) => alert(error))
            .finally(() => setLoading(false))
          )

      }
    }
  })
  .catch((error) => alert(error))
  .finally(() => setLoading(false));


 }, []);

EDIT 3: I added all 3 fetches in the same useEffect for convenience. I tried using await for the third fetch but it always goes into the catch.

useEffect(async () => {


fetch(geniusUrl + '/search?q=' + textSong, {
  headers: { 'Authorization': 'Bearer ' + geniusToken }
})
  .then((response) => response.json())
  .then((data) => {
    for (const value of data.response.hits) {
      if ((value.result.title.toLowerCase() == textSong.toLowerCase()) &&
        (value.result.primary_artist.name.toLowerCase() == textArtist.toLowerCase())) {
        setIdTitle(value.result.api_path)   //for third fetch
        setIdArtist(value.result.primary_artist.api_path)  
      }
    }
  })
  .catch((error) => { AlertView(), setModalVisible(true) })
  .finally(() => setLoading(false));


  fetch(lyricsUrl + textArtist + '/' + textSong)
    .then((response) => response.json())
    .then((text) => setDataLyrics(text.lyrics))
    .catch((error) => { AlertView(), setModalVisible(true) })
    .finally(() => setLoadingLyrics(false));


try {
  let response = await fetch(geniusUrl + idTitle, {
    headers: { 'Authorization': 'Bearer ' + geniusToken }
  });
  let id = await response.json();
  for (const value of id.response.song.media) {
    if (value.provider == 'youtube')
      setIdVideoYT(value.url)
  }
}
catch (error) {
  alert(error);
}
setLoading(false);

}, []);

outgg
  • 21
  • 5
  • Place the second fetch inside the first fetch's `then` callback. – Wais Kamal Apr 30 '21 at 16:49
  • or use `await`, since you're already marking your function as `async`. No need to use .then chaining, and makes your code so much easier to read, too. – Mike 'Pomax' Kamermans Apr 30 '21 at 16:52
  • Either put both fetches in the same effect, and just chain them, or make the second effect dependent on the `idTitle` state (and have it do nothing while it's still empty). – Bergi Apr 30 '21 at 16:54
  • where should I add `await`? @Mike'Pomax'Kamermans – outgg Apr 30 '21 at 16:58
  • I've also tried concatenating in the same effect, but it still gives me the problem. How do I make it dependent on idTitle? @Bergi – outgg Apr 30 '21 at 17:00
  • Keep giving me error @WaisKamal – outgg Apr 30 '21 at 17:01
  • Fairly straight forward change: `useEffect(async () => { try { response = await fetch(...); data = await response.json(); data.response?.hits?.forEach(...); } catch (e) { ... } setLoading(false); }, []);`, of course with some newlines in there. Note the `?.` notation so we only run `forEach` _if_ data.response.hits exists. Also note that `response.json()` _does not return JSON_, it _converts_ JSON to a real JS object, so calling your capture var "json" is always a bad idea. Name it based on what the data is. – Mike 'Pomax' Kamermans Apr 30 '21 at 17:07
  • @outgg Can you [edit] your question to include your attempt of chaining both fetch calls? We can't tell what went wrong without seeing the code. – Bergi Apr 30 '21 at 17:21
  • @outgg Btw your `geniusToken` is still visible in the edit history, make sure to invalidate it and get a new one - and remember not to store credentials in source code. – Bergi Apr 30 '21 at 17:21
  • I add the edit @Bergi – outgg Apr 30 '21 at 17:36
  • @Mike'Pomax'Kamermans I did not understand the part of the forEach, what would go inside? (is this useEffect for the second fetch?) – outgg Apr 30 '21 at 18:11
  • @outgg a) you need to pass a function to `then`, so () => fetch(…).…` b) [`idState` is a constant that doesn't get updated](https://stackoverflow.com/q/54069253/1048572), so you'll need to `value.result.api_path` instead. Easiest to move the second fetch call inside that `if` statement even. – Bergi Apr 30 '21 at 19:35
  • You'd put the code that's currently inside your for/of loop inside the `forEach`. You can also keep the for/of but if `media` is an array rather than an object, `forEach` makes a little more sense. – Mike 'Pomax' Kamermans Apr 30 '21 at 19:53
  • @Bergi I tried like you said, but maybe I'm wrong in the implementation (code in EDIT 2) – outgg May 03 '21 at 08:05
  • @outgg Remove `then(() => ` and replace it by `return`. You'd directly call `fetch().…` from inside the `if` statement, no "`then` function" (that doesn't exist). – Bergi May 03 '21 at 10:00
  • @Bergi I solved with concatenation. Thank you so much for your help. Thanks, everyone. – outgg May 03 '21 at 10:16

1 Answers1

0

As React Native again is a JS-based framework it pretty executes line by line as a non blocking model.

So to solve this ! const result = await fetch(URL, payload)

or

fetch(someUrl, Payload)
.then(res => res.json)
.then(res => {
Do something 
 fetch(someUrl)... etc
})
Giriraj
  • 41
  • 4