2

I'm working on a React app with Redux-Toolkit (RTK) Query. I need to fetchaccounts from the server and then render them as well derive some data from them through a chain of selectors. If I fetch them withcreateAsyncThunk and save in the store manually I can easily use them in any selector. I expected similar experience if I'm using RTK Query can't figure out how it works.

If I have a query without any parameters it works nicely:

export const apiSlice = createApi({  reducerPath: 'API',  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),  endpoints: (builder) => ({    getAccounts: builder.query<Account[], void>({      query: () => 'accounts',    }),  }),});export const getAccountsQuery = apiSlice.endpoints.getAccounts.select();export const getAccountsResult = createSelector(  [getAccountsQuery],  (accountsResult) => {    return accountsResult.data || [];  });

I can now usegetAccountsResult in any selector. However if the endpoint requires a parameter it's a different story:

export const apiSlice = createApi({  reducerPath: 'API',  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),  endpoints: (builder) => ({    getAccounts: builder.query<Account[], void>({      query: (orgId) => ({        url: 'accounts',        params: { orgId },      }),    }),  }),});

Now I need to passorgId toapiSlice.endpoints.getAccounts.select() and I can't do it inside a selector. Ideally I'd like it to be something like:

export const getAccountsResult = createSelector(  [getOrgId],  (orgId) => {    return apiSlice.endpoints.getAccounts.select(orgId).data || [];  });

but that's not how it works. Is there a way to achieve it?

And more broadly speaking, am I even supposed to be use RTK Query this way? There is a lot of resources on how to access and control data inside components but there's surprisingly little info on how to work with data in selectors.

Drew Reese's user avatar
Drew Reese
207k19 gold badges277 silver badges291 bronze badges
askedJun 13 at 0:27
user3272018's user avatar

1 Answer1

1

apiSlice.endpoints.getAccounts.select(orgId) creates a new selector function each time it's called, so it doesn't make for good use inline when trying to create a memoized selector function viacreateSelector. You can instantiate a memoized selector function using React'suseMemo though, and use this in youruseSelector/useAppSelector hook.

Example:

const selectAccountsByOrgId = React.useMemo(  () => apiSlice.endpoints.getAccounts.select(orgId),  [orgId],);const { data, ...rest } = useAppSelector(selectAccountsByOrgId);

You might not actually need to do that though when you could just use your generateduseGetAccountsQuery hook.

const { data, ...rest } = useGetAccountsQuery(orgId, /* options */);

Just make sure you are actually exporting the generated hooks:

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';export const apiSlice = createApi({  reducerPath: 'API',  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),  endpoints: (builder) => ({    getAccounts: builder.query<Account[], string>({      query: (orgId) => ({        url: 'accounts',        params: { orgId },      }),    }),  }),});export const { useGetAccountsQuery } = apiSlice;...

Since you can't use these as input selectors increateSelector you can abstract the logic into a custom hook if necessary.

Example:

const useQueryComplexAccountByOrgId = (orgId: string) => {  const selectAccountsByOrgId = React.useMemo(    () => apiSlice.endpoints.getAccounts.select(orgId),    [orgId],  );  const { data, ...rest } = useAppSelector(selectAccountsByOrgId);  // OR  // const { data, ...rest } = useGetAccountsQuery(orgId, /* options */);  const otherSelectedStateValue = useAppSelector(selectComplexState);  ...  // ... business logic};
const result = useQueryComplexAccountByOrgId(orgId);
answeredJun 13 at 6:26
Drew Reese's user avatar
Sign up to request clarification or add additional context in comments.

1 Comment

So there is no way to integrate it seamlessly into a chain of selectors from what I understand. I guess this approach works but it's pretty annoying that you now have to spread your logic between selectors and custom hooks, isn't it? Is there a particular reason for such implementation in RTK? I feel like the idea of having all your store data accessible in selectors is natural and intuitive, why make exceptions for fetched data? Either way thank you for your answer.

Your Answer

Sign up orlog in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to ourterms of service and acknowledge you have read ourprivacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.