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.
1 Answer1
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);1 Comment
Explore related questions
See similar questions with these tags.
