4

I would like to use the endpoint.select() function to create selectors from cached RTK Query data, see RTK Advanced Patterns.

The documentation clearly states that if there is no query argument, you can pass undefined to select() (see the Selecting Users Data section).

However, in my case this does not work unless i trigger the query by the initiate() function. When triggering the query from the query hook on the other hand, the selector fails to retrieve the cached data.

The not working setup here is pretty simple:

export const productsApi = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: API.ENDPOINTS.PRODUCTS }),
  reducerPath: 'productsApi',
  endpoints: (builder) => ({
    listAllProducts: builder.query({
      query: ()=>'/list',
    }),
  }),
});
export const { useListAllProductsQuery } = productsApi;

Then in a customHook I call the useListAllProducts hook:

const {
  data,
} = useListAllProductsQuery({skip:shouldSkip});

And finally in the selector:

export const selectProducts =
  productsApi.endpoints.listAllProducts.select(); //undefined param as docs recommend

Potential Fix: (or more like a hacky workaround): Strangely enough, I discovered that if i pass an argument (aka cacheKey) into the select function and pass that same cacheKey into the query hook, all of a sudden the stars align and everything works (although the docs state this is not necessary). So the modified code looks like:

// in selector
export const selectProducts =
  productsApi.endpoints.listAllProducts.select('products');

// in hook
const {
  data,
} = useListAllProductsQuery('products');

Im wondering if anyone can shed some wisdom on why this works, or even better can recommend the best practice for utilizing the select function on a query with no cacheKey (since the docs seem incorrect or outdated?).

I would also like to point out, when calling select() without a parameter, a typescript warning surfaces indicating a parameter is required.

DannyMoshe
  • 6,023
  • 4
  • 31
  • 53

2 Answers2

2

I am not certain where you think that the docs do state that you do not need an argument.

You will need to call select with the same argument as you call your hook to get a selector for that cache key - so if you call useMyQuery(), you can call select() - and if you call useMyQuery(5), you can call select(5) to get a selector for the cache key 5.

Those are individual cache entries and you will need a selector for each of those cache entries.

Also, could you clarify what exactly you mean by "not working"? Using the selector will give you only the cache entry, but not make a request - you are after all just selecting from the store. So before you used the hook or dispatched initiate, you will get an uninitiated cache entry.

phry
  • 35,762
  • 5
  • 67
  • 81
  • 2
    The docs state it in multiple locations (specifically speaking of queries which dont have an argument). See the section that i cited "Selecting Users Data". In my example i do not have a cacheKey, so i'm simply calling select() which is not working. What i mean by not working is that the selector fails to return any data from the cache. – DannyMoshe Dec 19 '21 at 21:53
  • So in your example `useMyQuery()` needs to become `useMyQuery('someDummyKey') ` and select(`someDummyKey`). The moment i make this change, it works as expected. – DannyMoshe Dec 19 '21 at 21:54
  • If that is true, it would be a bug - but to be honest, it is highly unlikely, since `useMyQuery()` also just calls `select()` internally to get exactly that same selector. So I guess something else would be going on. Could you create a CodeSandbox which shows this behaviour? – phry Dec 19 '21 at 22:15
  • 1
    Also, it seems very impossible that it does ever return nothing - it should at least return `{ status: "uninitialized", isUninitialized: true, is...: false }` – phry Dec 19 '21 at 22:18
  • Good point, actually i just checked and it is returning `{status: 'uninitialized', isUninitialized: true, isLoading: false, isSuccess: false, isError: false}`. I was looking at the output of a dependent selector. – DannyMoshe Dec 19 '21 at 22:33
  • I see the problem, I was using UseQueryStateOptions to add skip logic, yet I believe this will cause issues when there is no key passed in. Ive updated to the following which seems to work now: `shouldSkip ? skipToken : undefined` – DannyMoshe Dec 19 '21 at 23:33
  • 1
    Options have to always be the second argument, so if you wanted to use `skip` instead of `skipToken`, you would have to call `useMyQuery(undefined, { skip: true })`. Using `undefined` as argument would work with a `select()` call, `undefined` and no arg are considered equal – phry Dec 20 '21 at 07:26
0

I think, this can solve your problem

in component.jsx file

const state = useSelector((state: RootState)=>state);
console.log(getState(state, params));

in api.js file

export const getState = (state: RootState, params) => api.endpoints.getApiState.select(params)(state);
Dhruv
  • 51
  • 3