Blog>
Snippets

Integrating Intersection Observer with React Query

Provides code to set up an Intersection Observer that triggers the fetching of more data when a user scrolls to a specific part of the page, used in conjunction with React Query's infinite scrolling capabilities.
import { useInfiniteQuery } from 'react-query';
import { useInView } from 'react-intersection-observer';
Imports the necessary hooks from 'react-query' and 'react-intersection-observer'.
const fetchTodos = async ({ pageParam = 1 }) => {
  const response = await fetch(`https://example.com/api/todos?page=${pageParam}`);
  return response.json();
};
Defines the function for fetching todos from an API, which will be used with React Query's useInfiniteQuery hook.
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery('todos', fetchTodos, {
  getNextPageParam: (lastPage, pages) => lastPage.nextPage ?? false
});
Uses the useInfiniteQuery hook to fetch pages of todos, automatically handling pagination based on the response.
const { ref, inView } = useInView();
Sets up the useInView hook from 'react-intersection-observer' that provides a ref to attach to a DOM element and a state indicating if that element is in the viewport.
useEffect(() => {
  if (inView && hasNextPage) {
    fetchNextPage();
  }
}, [inView, hasNextPage, fetchNextPage]);
Uses the useEffect hook to call fetchNextPage when the observed element is in view and there are more pages of todos to fetch.
<div>
  {data.pages.map((page) => (
    <React.Fragment key={page.nextPageToken}>
      {page.results.map((todo) => (
        <div key={todo.id}>{todo.text}</div>
      ))}
    </React.Fragment>
  ))}
  <div ref={hasNextPage ? ref : null}>Loading more todos...</div>
</div>
Renders the list of todos, using a React Fragment for each page of data. A special div at the end of the list is given the ref from useInView, which triggers fetching the next page of todos when in view.