Reputation: 13
I am currently self teaching myself code and at the moment diving into React.js. I am building a simple app which displays beer information pulled through the punkAPI (https://punkapi.com/documentation/v2).
In my app I wanted to add some kind of "infinite" scroll which would pull more data from the API when the user reaches the bottom of the page.
At the moment, the handleScroll method works, and updates the state of the component but it does not trigger the render method and I was wondering why.
I realize that there are a lot of things wrong with my code and I plan to restructure it, add some booleans in this.state to check if there is still more data to load, to handle errors and to not trigger the handleScroll method so intensively.
However, I am still wondering why there is no rendering happening even though the state is updated.
class BeerList extends Component {
constructor() {
super()
this.state = {
loading: false,
beers: []
}
this.handleScroll = this.handleScroll.bind(this)
}
componentDidMount() {
window.addEventListener('scroll', this.handleScroll, true);
this.setState({
loading: true
})
fetch("https://api.punkapi.com/v2/beers?per_page=12")
.then(response => response.json())
.then(data => {
this.setState({
loading: false,
beers: data
})
})
}
handleScroll() {
const checkForNewBeers = () => {
let lastBeerCard = document.querySelector(".beer-list > div:last-child");
let lastBeerCardOffset = lastBeerCard.offsetTop + lastBeerCard.clientHeight;
let pageOffset = window.pageYOffset + window.innerHeight;
if (pageOffset <= lastBeerCardOffset - 10) {
return;
}
this.setState(prevState => {
const beers = prevState.beers;
const page = (prevState.beers.length / 12) + 1;
fetch(`https://api.punkapi.com/v2/beers?per_page=12&page=${page}`)
.then(response => response.json())
.then(data => {
for (let item of data) {
beers.push(item);
}
console.log({
beers: beers
});
return {
beers: beers
}
});
});
}
document.addEventListener("scroll", function (event) {
checkForNewBeers();
});
}
render() {
let beerCards = []
if (this.state.beers.length > 0) {
beerCards = this.state.beers.map(beer => {
return <BeerCard
key = {
beer.id
}
img = {
beer.image_url
}
title = {
beer.name
}
description = {
beer.description
}
/>
})
}
return ( <
div className = "container" >
<
div className = "row beer-list" > {
beerCards
} <
/div> < /
div >
)
}
}
export default BeerList
So BeerCards are correctly appended when the page is loading then when you scroll the console shows you that the state is updated (way too much but still). I would expect the page loading a shit ton of BeerCards but nothing is happening. Why is that?
Upvotes: 1
Views: 76
Reputation: 1178
You are so close! By simply adding the keyword "async" in front of "prevState" in "this.setState" and "return await" in front of "fetch", the state of your app should be updated and trigger a rerender as expected.
Upvotes: 0
Reputation: 5766
Instead of returning an object from your async fetch
, call setState
inside the .then()
let beers = this.state.beers;
const page = (this.state.beers.length / 12) + 1;
fetch(`https://api.punkapi.com/v2/beers?per_page=12&page=${page}`)
.then(response => response.json())
.then(data => {
for (let item of data) {
beers.push(item);
}
this.setState({ beers });
});
Upvotes: 1
Reputation: 5402
As soon as you invoke fetch which is the last statement inside setState, an undefined would be returned. Try transforming the setState parameter to an async function:
this.setState(async prevState => {
const beers = prevState.beers;
const page = prevState.beers.length / 12 + 1;
let response = await fetch(
`https://api.punkapi.com/v2/beers?per_page=12&page=${page}`
);
response = await response.json();
for (const item of response) {
beers.push(item);
}
console.log({
beers: beers
});
return {
beers: beers
};
});
Upvotes: 1