Already implemented an infinite scroll feature to my recipes app using Edamam API, but I have this issue:
When I reach the bottom of the page and it starts to fetch the next page, the component does a total refresh, it does show all the items of each page after tho.
What I want is that when I reach the bottom, it starts fetching (showing my spinner at the bottom and stuff) and render the next page at the bottom without refreshing the whole page at all.
Here is my code:
const RecipesGrid = ({ query }) => {
const getRecipes = async (pageParam) => {
try {
const path = pageParam
? pageParam
: `https://api.edamam.com/api/recipes/v2?q=${query}&app_id=${process.env.REACT_APP_APP_ID}&app_key=${process.env.REACT_APP_API_KEY}&type=public`;
const response = await axios.get(path);
return response.data;
} catch (error) {
console.log(error);
}
};
useEffect(() => {
const onScroll = (event) => {
const { scrollHeight, scrollTop, clientHeight } =
event.target.scrollingElement;
if (scrollHeight - scrollTop <= clientHeight * 1.2) {
fetchNextPage();
}
};
document.addEventListener("scroll", onScroll);
return () => document.removeEventListener("scroll", onScroll);
// eslint-disable-next-line
}, []);
const {
data,
isFetching,
isFetchingNextPage,
status,
hasNextPage,
fetchNextPage,
} = useInfiniteQuery(
["recipes", query],
({ pageParam = "" }) => getRecipes(pageParam),
{
getNextPageParam: (lastPage) =>
lastPage._links.next ? lastPage._links.next.href : undefined,
}
);
if (isFetching || status === "loading") {
return <Spinner />;
}
if (status === "error") {
return <div>Whoops! something went wrong...Please, try again</div>;
}
return (
<div className={styles.Recipes}>
{data?.pages.map((item, index) => (
<React.Fragment key={index}>
{item.hits.map((recipe) => (
<Recipe recipe={recipe} key={recipe.recipe.uri} />
))}
{item.hits.length === 0 ? (
<Empty message="No results found!" />
) : null}
{!hasNextPage && item.hits.length !== 0 && (
<Empty message="No more recipes!" />
)}
</React.Fragment>
))}
{isFetchingNextPage && <Spinner />}
</div>
);
};
export default RecipesGrid;
CodePudding user response:
I think you're unmounting the whole list because you only render a loading spinner here:
if (isFetching || status === "loading") {
return <Spinner />;
}
isFething
is always true whenever a request is in-flight. This is also true for when you are fetching the next page, so you get into this early return and remove your list. Removing isFetching
from the if statement should solve it, since you display a background loading indicator anyways via:
{isFetchingNextPage && <Spinner />}
Additionally, your error handling won't work because you transform errors into resolved promises by catching them for logging and not re-throwing them:
} catch (error) {
console.log(error);
}
Please use the onError
callback for things like that.