react-snap-carousel icon indicating copy to clipboard operation
react-snap-carousel copied to clipboard

Best practice for infinite items & API fetching?

Open 0xTenzo opened this issue 7 months ago • 1 comments

More of a question than an issue, but seemed like it would be an interesting topic.

In a typical example, let's say we want to list the most recent items first, here is my naive way to do it:

  • (1) On initial page load we fetch the top 10 most recent items.
  • (2) If the user requests items that are later than the top 10, then we make an API call to fetch 10 more. This can be made seamless by triggering it when the arrow is clicked when the last item is already active: image

Though one initial thought I have is that I'd like there to be more of a "infinite" feel for the user. The initial listing of N many dots in image makes it feel like there are actually a limited amount of items, rather than infinite.

Maybe it would be better to remove the dots entirely, and have internal logic to do "if item is already present, scroll to it, else do an API call then scroll to it once loaded". This solution seems ideal to me, but I am not certain how to configure react-snap-carousel to do this.

Would love to get your thoughts @richardscarrott @pedropalau, thank you so much for this awesome resource!

0xTenzo avatar Nov 24 '23 15:11 0xTenzo

Hi @0xTenzo,

You should certainly be able to do this with react snap carousel. This isn't tested, but this might be a starting point for you:

const Carousel = () => {
  const { activePageIndex, pages, refresh, next, prev, snapPointIndexes } = useSnapCarousel();
  const { data, fetchMore } = useApi('/items'); // pseudo code
  const isLastCarouselPage = activePageIndex === pages.length - 1;
  useLayoutEffect(() => {
    refresh();
  }, [data.items]);
  return (
    <div>
      <ul
        className="scrollbar-hidden relative flex h-[100svh] w-full snap-x snap-mandatory overflow-auto"
        ref={scrollRef}
      >
        {data.items.map((item, i) => (
          <li
            key={item.id}
            className={classNames("h-full w-full shrink-0", {
              "snap-start": snapPointIndexes.has(i),
            })}
          >
            <h2>{item.title}</h2>
          </li>
        ))}
      </ul>
      <button onClick={() => prev()}>Prev</button>
      <button onClick={async () => {
        if (isLastCarouselPage && data.hasNextPage) {
           await fetchMore(); // This may need to `flushSync` to ensure the DOM has updated before calling `next`.
        }
        next(); // May have an issue with stale closure here 🤔
      }}>Next</button>
    </div>
  );
}

richardscarrott avatar Dec 02 '23 11:12 richardscarrott