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;
