Home > Blockchain >  React query paginated query not enabling next button as per example
React query paginated query not enabling next button as per example

Time:12-21

I have followed this example:

https://react-query-v3.tanstack.com/guides/paginated-queries

However, when I have tried doing it myself, the next button is always disabled? so I can't click next. my code is below:

Stackblitz example

import React, { useState } from 'react';
import { render } from 'react-dom';
import { useQuery } from 'react-query';
import axios from 'axios';
import { QueryClient, QueryClientProvider} from 'react-query';

const App: React.FunctionComponent = () => {
  const queryKey = 'getData';
  const [page, setPage] = React.useState(1)

  const getDataFunction = async (page): Promise<any> => {
    const response = await axios
      .get(
        `https://jsonplaceholder.typicode.com/posts?_limit=10&_page=${page}`
      )
      .then((res) => res.data);
    return response;
  };

  const {
    data: result,
    isFetching,
    status,
    error,
    refetch,
    isPreviousData,
  }: any = useQuery([queryKey, page], () => getDataFunction(page), {
    keepPreviousData : true,
    refetchOnWindowFocus: false,
  });

  return (
    <div className="Container" >
      <button onClick={refetch}>Refetch query</button>
      {status === 'error' && <div className="mt-5">{error.message}</div>}
      {isFetching ? (
        <div className="mt-5">Loading data ...</div>
      ) : (
        <div style={{height: 'auto', border: '1px solid', padding: '15px', margin: '10px 0px 10px 0px'}}>
          {status === 'success' && (
            <div>
              <p>
                {result?.map((inner, index) => {
                  return (
                    <div key={index}>
                      <p>
                        {inner?.id}
                        <br />
                        <strong>{inner?.title}</strong> <br />
                        {inner?.body}
                      </p>
                    </div>
                  );
                })}
              </p>
              <p>{result?.title}</p>
            </div>
          )}
        </div>
      )}
            <span>Current Page: {page}</span>
       <button
         onClick={() => setPage(old => Math.max(old - 1, 0))}
         disabled={page === 1}
       >
         Previous Page
       </button>{' '}
       <button
         onClick={() => {
           if (!isPreviousData && result?.hasMore) {
             setPage(old => old   1)
           }
         }}
         // Disable the Next Page button until we know a next page is available
         disabled={isPreviousData || !result?.hasMore}
       >
         Next Page
       </button>
    </div>
  );
};

const queryClient = new QueryClient();

render(
  <QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>,
  document.getElementById('root')
);

Any idea's?

CodePudding user response:

In their example the response from the server contained hasMore flag, in your case you don't have it, you have just set of data. So the only way you could check if the next page is available is by checking if length of the data returned to you is equals page size (it is not perfect, because it might be that the last available page contains 10 items, so the next page will have 0, but it is the only thing you can do here).

So basically you need

const hasNext = result?.length == 10;

And then use it

  <button
    onClick={() => {
      if (!isPreviousData && hasNext) {
        setPage((old) => old   1);
      }
    }}
    // Disable the Next Page button until we know a next page is available
    disabled={!hasNext}
  >
    Next Page
  </button>

See https://stackblitz.com/edit/react-ts-ggyyg8?file=index.tsx

CodePudding user response:

hasMore is not a value from react-query, it should be returned from your server to tell your frontend that you have more data in the next page.

Your example API does not return anything in result.hasMore, that is why the button has remained disabled.

I assume typicode returns a max of 100 items, so you can do this to use the limit and page number as a reference

import React, { useState } from 'react';
import { render } from 'react-dom';
import { useQuery } from 'react-query';
import axios from 'axios';
import { QueryClient, QueryClientProvider} from 'react-query';

const App: React.FunctionComponent = () => {
  const queryKey = 'getData';
  const [page, setPage] = React.useState(1)
  const [limit] = React.useState(10)

  const getDataFunction = async (page): Promise<any> => {
    const response = await axios
      .get(
        `https://jsonplaceholder.typicode.com/posts?_limit=${limit}&_page=${page}`
      )
      .then((res) => res.data);
    return response;
  };

  const {
    data: result,
    isFetching,
    status,
    error,
    refetch,
    isPreviousData,
  }: any = useQuery([queryKey, page], () => getDataFunction(page), {
    keepPreviousData : true,
    refetchOnWindowFocus: false,
  });

  return (
    <div className="Container" >
      <button onClick={refetch}>Refetch query</button>
      {status === 'error' && <div className="mt-5">{error.message}</div>}
      {isFetching ? (
        <div className="mt-5">Loading data ...</div>
      ) : (
        <div style={{height: 'auto', border: '1px solid', padding: '15px', margin: '10px 0px 10px 0px'}}>
          {status === 'success' && (
            <div>
              <p>
                {result?.map((inner, index) => {
                  return (
                    <div key={index}>
                      <p>
                        {inner?.id}
                        <br />
                        <strong>{inner?.title}</strong> <br />
                        {inner?.body}
                      </p>
                    </div>
                  );
                })}
              </p>
              <p>{result?.title}</p>
            </div>
          )}
        </div>
      )}
            <span>Current Page: {page}</span>
       <button
         onClick={() => setPage(old => Math.max(old - 1, 0))}
         disabled={page === 1}
       >
         Previous Page
       </button>{' '}
       <button
         onClick={() => {
           if (!isPreviousData && (page * limit) < 100) {
             setPage(old => old   1)
           }
         }}
         // Disable the Next Page button until we know a next page is available
         disabled={isPreviousData || (page * limit) >= 100}
       >
         Next Page
       </button>
    </div>
  );
};

const queryClient = new QueryClient();

render(
  <QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>,
  document.getElementById('root')
);

  • Related