Infinite Scroll Hooks

The recommended hooks to use for a Infinite Scroll behaviour are:

  • useArchiveInfiniteScroll

  • usePostTypeInfiniteScroll

There's also another one available for implementing custom infinite scroll hooks (used internally by the previous two hooks):

  • useInfiniteScroll

useInfiniteScroll is not intented to be used directly by theme developers unless they are creating their own infinite scroll logic. Use useArchiveInfiniteScroll or usePostTypeInfiniteScroll instead.

The main idea behind these hooks is that they return a list of Wrapper components, one for each entity listed while scrolling, that handle both the route updating and fetching of the next entity.

Table of Contents

useArchiveInfiniteScroll

This hook implements the logic needed to include infinite scroll in archives (i.e. categories, tags, the posts archive, etc.).

The hook receives options to set a limit of pages shown automatically, to disable it, and also settings for the intersection observers that are passed to the useInfiniteScroll hooks used internally.

useArchiveInfiniteScroll is designed to be used inside an Archive component. That component would render all the archive pages from the pages returned by the hook.

In addition to the above, the hook returns a set of boolean values that indicate if the next page is being fetched, if the limit has been reached, or if the next page returned an error, and a function that allows the next page to be fetched manually.

Parameters

It accepts an optional object with the following props:

The IntersectionOptions type refers to the type of the the parameters received by the useInView hook.

Return value

An object with the following properties:

Each element of the pages array has the following structure:

Usage

import { connect } from "frontity";
import useArchiveInfiniteScroll from "@frontity/hooks/use-archive-infinite-scroll";
import ArchivePage from "./archive-page";

/**
 * Simple component showing the usage of the `useArchiveInfiniteScroll` hook.
 *
 * @example
 * ```
 * // In the Theme component:
 * <Switch>
 *   {...}
 *   <Archive when={data.isArchive} />
 * </Switch>
 * ```
 */
const Archive = () => {
  // Get the list of pages from the hook.
  const {
    pages,
    isFetching,
    isLimit,
    isError,
    fetchNext,
  } = useArchiveInfiniteScroll({ limit: 3 });

  return (
    <>
      {pages.map(({ Wrapper, key, link, isLast }) => (
        <Wrapper key={key}>
          <ArchivePage link={link} />
          {!isLast && <PageSeparator />}
        </Wrapper>
      ))}

      {isFetching && <div>Loading more...</div>}

      {(isLimit || isError) && (
        <button onClick={fetchNext}>
          {isError ? "Something failed - Retry" : "Load More"}
        </button>
      )}
    </>
  );
};

export default connect(Archive);

usePostTypeInfiniteScroll

Hook that implements the logic needed to include infinite scroll in a post type view (i.e. posts, pages, galleries, etc.).

This hook is more complex than the previous one, as it works by getting the post type entities from the specified archive and thus it doesn't fetch the next post but the next page of posts.

It recevies an archive and a fallback prop ―both links―, to specify the source of the post entities. If none of them is specified, state.source.postsPage is used. When the penultimate post of the first page is rendered, the next page of the archive is fetched. A list of the fetched pages is stored in the browser history state along with the list of posts.

The limit prop in this case stands for the number of posts being shown, not the number of fetched pages (as in the case of useArchiveInfiniteScroll). In the same way, the fetchNext shows the next post, and only fetches the next page of posts if needed.

Parameters

It accepts an optional object with the following props:

The IntersectionOptions type refers to the type of the the parameters received by the useInView hook.

Return value

The output of these hooks is pretty similar to the previous one's:

Each element of the posts array has the following structure:

Usage

import { connect } from "frontity";
import usePostTypeInfiniteScroll from "@frontity/hooks/use-post-type-infinite-scroll";
import PostTypeEntity from "./post-type-entity";

/**
 * Simple component showing the usage of the `usePostTypeInfiniteScroll` hook.
 *
 * @example
 * ```
 * // In the Theme component:
 * <Switch>
 *   {...}
 *   <PostType when={data.isPostType} />
 * </Switch>
 * ```
 */
const PostType = () => {
  // Get the list of posts from the hook.
  const {
    posts,
    isFetching,
    isLimit,
    isError,
    fetchNext,
  } = usePostTypeInfiniteScroll({ limit: 5 });

  return (
    <>
      {posts.map(({ Wrapper, key, link, isLast }) => (
        <Wrapper key={key}>
          <PostTypeEntity link={link} />
          {!isLast && <PostSeparator />}
        </Wrapper>
      ))}

      {isFetching && <div>Loading more...</div>}

      {(isLimit || isError) && (
        <button onClick={fetchNext}>
          {isError ? "Something failed - Retry" : "Load More"}
        </button>
      )}
    </>
  );
};

export default connect(PostType);

Demo

This short video demonstrates the usage of the Infinite Scroll Hooks avalable at the @frontity/hooks package.

The project used in the video is available here.

useInfiniteScroll

useInfiniteScroll is not intented to be used directly by theme developers unless they are creating their own infinite scroll logic. Use useArchiveInfiniteScroll or usePostTypeInfiniteScroll instead.

This is the core hook with the basic logic to build an infinite scroll hook.

It basically receives two links, currentLink and nextLink, and returns two React refs that should be attached to react elements. The hook uses useInView internally to track the visibility of those elements and trigger an actions.router.set to update the current link or an actions.source.fetch to fetch the next entity. You can pass options for these useInView hooks as well, using the fetchInViewOptions and the routeInViewOptions params.

useInfiniteScroll also keeps a record of the fetched & ready entities in the browser history state, in order to restore the list when you go back and forward while navigating. That record is accessible from the browser history state under the infiniteScroll.links array.

Note: the history state is also accessible from the Frontity state, in state.router.state.

It was designed to be used inside a Wrapper component that would wrap the entity pointed by currentLink.

Parameters

It requires an object with the following props:

The IntersectionOptions type refers to the type of the the parameters received by the useInView hook.

Return value

Usage

Note: this is just an example to illustrate how the useInfiniteScroll works. For better examples, see the useArchiveInfiniteScroll and the usePostTypeInfiniteScroll implementation.

import { useConnect, connect, css } from "frontity";
import useInfiniteScroll from "@frontity/hooks/use-infinite-scroll";
import { isArchive, isError } from "@frontity/source";

export const wrapperGenerator = ({
  link,
  fetchInViewOptions,
  routeInViewOptions,
}) => {
  const Wrapper = ({ children }) => {
    const { state } = useConnect();

    const current = state.source.get(link);
    const next =
      isArchive(current) && current.next
        ? state.source.get(current.next)
        : null;

    const { supported, fetchRef, routeRef } = useInfiniteScroll({
      currentLink: link,
      nextLink: next?.link,
      fetchInViewOptions,
      routeInViewOptions,
    });

    if (!current.isReady || isError(current)) return null;
    if (!supported) return children;

    const container = css`
      position: relative;
    `;

    const fetcher = css`
      position: absolute;
      width: 100%;
      bottom: 0;
    `;

    return (
      <div css={container} ref={routeRef}>
        {children}
        {<div css={fetcher} ref={fetchRef} />}
      </div>
    );
  };

  return connect(Wrapper);
};

Last updated