Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React state update (SWR) and component refresh?

I'm using SWR to fetch data as stated in the docs:

function App () {
  const [pageIndex, setPageIndex] = useState(0);

  // The API URL includes the page index, which is a React state.
  const { data } = useSWR(`/api/data?page=${pageIndex}`, fetcher);

  // ... handle loading and error states

  return <div>
    {data.map(item => <div key={item.id}>{item.name}</div>)}
    <button onClick={() => setPageIndex(pageIndex - 1)}>Previous</button>
    <button onClick={() => setPageIndex(pageIndex + 1)}>Next</button>
  </div>
}

The only difference in my case is I'm loading more items on the same page (so more like infinite scroll not pagination), but here's the trouble:

const { data } = useSWR(`/api/data?page=${pageIndex}`, fetcher);

Notice how data is fetched based on pageIndex variable, that comes from the state. Once it changes everything rerenders, so instead of getting more items every time user clicks on a button I'm always getting a refresh, then user sees initial render, then new items. So in short I want to load more items at the bottom of the page, not refresh everything and then add the items... What am I missing? There's useSWRInfinite, but it's the same story, it gets url data from the state...

like image 420
Wordpressor Avatar asked Aug 28 '26 10:08

Wordpressor


1 Answers

I would cache all the previously fetched pages in local state. Use a useEffect hook with a dependency on pageIndex state and current data value, store the data in an object using the current pageIndex as the key. When rendering all the fetched pages flatten the array of pages values to a single renderable array.

function App () {
  const [pageIndex, setPageIndex] = useState(0);
  const [pages, setPages] = useState({});

  // The API URL includes the page index, which is a React state.
  const { data } = useSWR(`/api/data?page=${pageIndex}`, fetcher);

  useEffect(() => {
    // cache page in state
    setPages(pages => ({
      [pageIndex]: data,
    }));
  }, [data, pageIndex]);

  // ... handle loading and error states

  // NOTE: pages is an object of arrays, so flatten values to single array to render

  return <div>
    {Object.values(pages).flatten().map(item => (
      <div key={item.id}>{item.name}</div>
    ))}
    <button onClick={() => setPageIndex(page => page - 1)}>Previous</button>
    <button onClick={() => setPageIndex(page => page + 1)}>Next</button>
  </div>
}
like image 88
Drew Reese Avatar answered Aug 29 '26 23:08

Drew Reese



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!