skip to Main Content

I have an RTK Query endpoint that gets a list of data, and I have other components that uses the same data returned from the query. One component uses a subset of the list with some common attribute, and another component shows details of an item from the list — getById. These components are not rendered in order; any of the components can render first.

How can I utilized the same results in all of these components, and only fetch once or when the results has been invalidated?

I tried using selectFromResults, but this only assumes that the data has been fetched from a parent component (doesn’t really trigger fetching — unless I’m wrong). If I’m not wrong, the RTK team should probably consider a patch for this issue?

I know I can call the query in all the components and it’ll just get the cached data. I’m just wondering if there’s a better solution.

I can also copy the data to the redux store and manage it myself, and off course I would be missing out on the benefits of RTKQ, plus this will also create duplicate.

2

Answers


  1. You’re right that the most efficient way is to use the query wherever you need it. If you add another hook that hits the same endpoint with the same parameters, both will use the same cache entry. That’s the most efficient way to do it. selectFromResults should trigger a query if it hasn’t already run, or you can use createSelector to create a memoized selector and just use it inline on your returned data.

    Login or Signup to reply.
  2. You can utilize upsertQueryData and manage the cache entries by yourself if possible depending on which data was fetched first – a list of items, or single item by id.

    Basing on your question, I assume you have two endpoints returning one or n of the same items

    1. getPostById – which returns single post item of type: interface Post {}
    2. getPostsList – which returns a list of items with the same shape as getPostById of type: type PostsList = Post[]

    Then, if getPostsList query returns data, we can easily use upsertQueryData to manually create cache entries for each getPostById to prevent refetching it on enter the <Post/> component:

    // in a postApi.ts
       getPostsList: builder.query(
          {
            query: ({ limit, page }) => {
              return {
                url: `${getPostsUrl}?limit=${limit}&page=${page}`,
                method: 'GET',
              };
            },
            // here comes magic of manual cache entries creation
            onQueryStarted: async (queryArgs, { dispatch, queryFulfilled }) => {
              // pessimistic update, wait for response to manually create cache
              const { data } = await queryFulfilled;
              data.forEach((post) => {
                dispatch(
                  // update or insert cache entries for getPostById({ id })
                  postApi.util.upsertQueryData(
                    'getPostById', // endpoint name
                    { id: post.id }, // arguments
                    post, // complete data inserted in the cache entry, there's no need to request for it again
                  ),
                );
              });
            },
          }),
    
    

    Now, if you first load a list component <PostsList/> requesting for getPostsList and then you enter a <Post/> component requesting for getPostById (where you have postApi.endpoints.getPostById.useQuery({ id })), no request is fired, because you already have all data in the cache (from pessimistic update based on getPostsList response).

    It’s the only scenario when you can prefill the cache data. If first you request for getPostById and later for a list, there is no space for optimization because frontend has no clue that previously requested by id posts will be in the list or not.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search