Home > database >  React fetch, async, i never know when it goes from undefined to ok
React fetch, async, i never know when it goes from undefined to ok

Time:01-31

I'm pretty frustrated because I don't know when I for sure set a State and then I can work with that state on other sections of my App. What I am trying to do is get 40 photos from an API, and set them to a state called photoList.

And then setting only 10 of these to be visible and render them. After that I want to do pagination so I can change states and show the 10-20 photos, 20-30 photos, etc.

function PhotoList() {
  
  const [photoList, setPhotoList] = useState([]);
  const [visiblePhotos, setVisiblePhotos] = useState([]);
  const [title, setTitle] = useState("hello");

  useEffect(() => {
    requestPhotos();
    loadFirstTen();
  }
  , []);


  const loadFirstTen = () =>{

    let photos = [];
    
    for(let i=0; i<10; i  ){
      photos.push(photoList[i]);
    }
    setVisiblePhotos(photos);

  }

  async function requestPhotos(){

    const url = "https://jsonplaceholder.typicode.com/photos"
    const response = await fetch(url);
    const json = await response.json();

    let Photos = [];

    for(let i=0; i<40; i  ){
      Photos.push(json[i]);
    }

    setPhotoList(Photos);
    
  }

  const listItems = visiblePhotos.map((photo) => 
    <div className="bottom-40 display-inline" >
      <MediaCard title={photo.title} image={photo.url} className="display-inline"/>
    </div>
  );
  
  return (
    <div className="App">
      <h1>{title}</h1>
      {listItems}
    </div>
  );
}

export default PhotoList;

I really do not understand. If i put everything inside the requestPhotos function, it works, and I can set the first ten photos. But if I do it outside that function, after I have already set the complete list of photos, it says "undefined".

I am trying to separate things and do separate functions so it does not all go on one go, but I really do not understand what is going on. Plus, I have no certainty that if I call other functions for example after someone clicks on the page 2 button, that photoList will effectively have the 40 photos, and that I will not get an undefined message.

Thanks a lot in advance.

CodePudding user response:

I think the problem is that you are calling a async in a useEffect. Try to handle the promise with .then and .catch and set your photos in the .then function. This makes sure that the photos are fetched.

CodePudding user response:

You have to make sure to load firstTen and more only once your photoList is filled after the API Call. I would suggest to manage and call your first and next photos conditionally within the useEffect().

Here you can have a look on a working sample App.

Just for demonstration purposes I replaced your MediaCard with a <p>.

import { useState, useEffect } from "react";

function PhotoList() {
  const [photoList, setPhotoList] = useState([]);
  const [visiblePhotos, setVisiblePhotos] = useState([]);
  const [title, setTitle] = useState("hello");
  const [page, setPage] = useState(0);

  async function requestPhotos() {
    const url = "https://jsonplaceholder.typicode.com/photos";
    const response = await fetch(url);
    const json = await response.json();

    let Photos = [];

    for (let i = 0; i < 40; i  ) {
      Photos.push(json[i]);
    }

    setPhotoList(Photos);
  }

  const loadFirstTen = () => {
    let photos = [];

    for (let i = 0; i < 10; i  ) {
      photos.push(photoList[i]);
    }
    setVisiblePhotos(photos);
    setPage(1);
  };

  const loadNextTen = () => {
    let photos = [];

    for (let i = 0; i < 10; i  ) {
      photos.push(photoList[i   10 * page - 10]);
    }
    setVisiblePhotos(photos);
  };

  useEffect(() => {
    !photoList.length && page < 2 && requestPhotos();
    photoList.length && page < 2 && loadFirstTen();
    page > 1 && loadNextTen();
  }, [photoList, page]);

  const listItems = visiblePhotos.map((photo) => (
    <div className="bottom-40 display-inline">
      <p title={photo.title} image={photo.url} className="display-inline">
        {photo.title}
      </p>
    </div>
  ));

  return (
    <div className="App">
      <h1>{title}</h1>
      {listItems}
      <button onClick={() => setPage(page   1)}>next</button>
    </div>
  );
}

export default PhotoList;
  •  Tags:  
  • Related