0

When page changes, new query is created and it's data is set to initialData. In this case user sees initialData before new query data is fetched:

import React from "react";

import fetch from "../lib/fetch";
import useSWR from "swr";

function Component({ initialData }) {
  const [page, setPage] = React.useState(1);
  const { data } = useSWR(
    `/api/data?page=${page}`,
    { initialData }
  );
  // ...
}
Component.getServerSideProps = async () => {
  const data = await fetch('/api/data?page=1');
  return { initialData: data };
};

My issue is that initialData is used as a fallback every time I query for new data:

enter image description here

Do you have any ideas on how I can prevent this flicker?

Ioan Ungurean
  • 319
  • 1
  • 15
  • interesting that you tagged this with `react-query`, because I don't think its an issue that exists in `react-query` ;) – TkDodo May 17 '21 at 11:28
  • Initially I was using `react-query` but moved to `swr` because of this issue - unfortunately, both have the same issue. :( – Ioan Ungurean May 17 '21 at 11:40

1 Answers1

1

So in react-query, I think there are multiple ways to avoid this:

  1. keepPreviousData: true

this is the main way how to do pagination, also reflected in the docs as well as the examples.

It will make sure that when a query key changes, the data from the previous queryKey is kept on the screen while the new fetch is taking place. The resulting object will have an isPreviousData flag set to true, so that you can e.g. disable the next button or show a little loading spinner next to it while the transition is happening. It is similar in ux what react suspense is going to give us (somewhen).

  1. initialData function

initialData accepts a function as well, and you can return undefined if you don't want initial data to be present. You could tie this to your page being the first page for example:

function Component({ initialData }) {
  const [page, setPage] = React.useState(1);
  const { data } = useQuery(
    ['page', id],
    () => fetchPage(id),
    { initialData: () => page === 1 ? initialData : undefined }
  );
}

keep in mind that you will have a loading spinner then while transitioning, so I think this is worse than approach 1.

  1. since your initialData comes from the server, you can try hydrating the whole queryClient. See the docs on SSR.

initialData works very well with keepPreviousData, so here is a codesandbox fork of the example from the doc where initialData is used (solution 1). I think this is the best take: https://codesandbox.io/s/happy-murdock-tz22o?file=/pages/index.js

TkDodo
  • 20,449
  • 3
  • 50
  • 65