Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to fetch all the responses stored in api slice [RTK Query]?

Here's the hook which returns the data based on the page

    const {
        data,
        isFetching,
        isLoading,
        isError,
    } = useGetResourceQuery(page, perPage );

here's the api

    export const api = createApi({
        baseQuery: fetchBaseQuery({
            baseUrl: "http://localhost:3001",
        
        }),
        tagTypes: ["resource"],
        endpoints: (build) => ({
            getResource: build.query({
            query: (page = 1, perPage = 20) =>
                `resource?spage=${page}&limit=${perPage}`,
            }),
        }),
    });
    export const {useGetResourceQuery} = api

Is there any way we can extract the state of all the queries? I want to implement pagination based on scroll (infinite scroll) Do we need to update the middleware? or are there any ways to access the state and expose it as a function above, I went through the documentation but couldn't find a solution

I can use local state and concatenate them but wanted to know if it's possible using RTK

Thanks

like image 874
Avinash Avatar asked Jun 09 '21 17:06

Avinash


People also ask

Is RTK a query?

RTK Query is a powerful data fetching and caching tool. It is designed to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.

Is RTK query good?

RTK Query can be added as a middleware and provides powerful React Hooks to help fetch your data. Despite its nascence, it's safe to say that when RTK Query goes into production, it will be a perfect candidate to fetch data in JavaScript apps of all sizes.

Can I use react query with Redux toolkit?

Now you can, by using Redux Toolkit and its latest addition: RTK Query. RTK Query is an advanced data-fetching and client-side caching tool. Its functionality is similar to React Query but it has the benefit of being directly integrated with Redux.


2 Answers

I think most implementations overcomplicate the problem of "infinite scroll" by a lot. You can achieve this by stepping a bit back and thinking about what you really need:

  • the current in-view data
  • a bit extra data into the direction we're scrolling to

Since we are lazy and do not want to track which direction we're scrolling to, just assume

  • the current in-view data
  • a bit extra data into both directions.

Also let's assume the query returns a response like

  {
    offset: 50,
    items: [...]
  }

So assuming one page is big enough to contain all the data for a screen, we'd end up with something like

const currentPage = // something calculated from ScrollPosition

const lastResult = usePageQuery(currentPage - 1, { skip: currentPage === 1 }) // don't fetch pages before 0
const currentResult = usePageQuery(currentPage)
const nextResult = usePageQuery(currentPage + 1)

const combined = useMemo(() => {
  const arr = new Array(pageSize * (currentPage + 1))
  for (const data of [lastResult.data, currentResult.data, nextResult.data]) {
    if (data) {
      arr.splice(data.offset, data.items.length, ...data.items)
    }
  }
  return arr
}, [pageSize, currentPage, lastResult.data, currentResult.data, nextResult.data])

// work with `combined` here

Since requests that are not referenced by your component, will stay in the store for 60 seconds, the user can scroll up quickly through many pages without further requests - but also, data that will probably never scrolled to again will be removed from the cache and simply re-fetched when necessary.

like image 162
phry Avatar answered Nov 07 '22 01:11

phry


You can use queryFn to assemble the cached results you already have. remember to use invalidation to get the updated state.

In the following code, the first queryFn is just a fake API, and the second endpoint is the solution you are looking for:

export const songsApi = createApi({
  reducerPath: "songsApi",
  baseQuery: fetchBaseQuery(),
  endpoints: (builder) => ({
    getSongs: builder.query<Result<Song>, { offset: number; limit: number }>({
      queryFn: ({ offset, limit }) => {
        let list: Song[] = [];
        for (let i = offset; i < offset + limit; i++) {
          list.push({ id: i.toString(), name: `Song ${i}` });
        }
        return new Promise((resolve) => {
          setTimeout(() => {
            resolve({
              data: {
                total: 100,
                list
              } as Result<Song>
            });
          }, 1000);
        });
      }
    }),
 
    getAllSongs: builder.query<Result<Song>, void>({
      queryFn: (_, { getState }) => {
        let state = getState();
        let songs: Song[] = [];
        for (let offset = 0; offset < 100; offset += 10) {
          const { data } = songsApi.endpoints.getSongs.select({
            offset,
            limit: 10
          })(state as any);
          if (data && data.list) {
            songs.push(...data!.list);
          }
        }
        return Promise.resolve({ data: { total: 100, list: songs as Song[] } });
      }
    })
  })
});

interface Result<T> {
  total: number;
  list: T[];
}
like image 20
HassanHeydariNasab Avatar answered Nov 07 '22 02:11

HassanHeydariNasab