paint-brush
More Features of the RTK Queryby@anatolii
1,560 reads
1,560 reads

More Features of the RTK Query

by Anatolii KabanovMay 24th, 2022
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

API has two endpoints that handle requests and receive data. Hook name consists of the prefix ‘use’, the endpoint name ‘fetchConfigDetails’ and the postfix of the request’s type (a function that is used to create the endpoint) ‘build.query’ The main difference between them is the argument ‘id’ so actually the resources are different. The cache will be also different, one for ==ConfigDetails== and one for !=ConfigRunDetails.

Company Mentioned

Mention Thumbnail
featured image - More Features of the RTK Query
Anatolii Kabanov HackerNoon profile picture

Previously I wrote about RTK, this story will cover other details of its use.


Imagine we need to display the same kind of data but from different resources depending on the incoming parameter value. In this case, we will have the component with two hooks that handle requests and receive data.


Basically, the representation of API looks like this:


export const configApi = baseApi.injectEndpoints({
    endpoints: build => ({
        fetchConfigDetails: build.query<IConfig[], IBaseRequest>({
            query: arg => `/config-details`,
            transformResponse: (x: IResponse<IConfig[]>) => x.result,
            providesTags: ['ConfigDetails'],
        }),
        fetchConfigRunDetails: build.query<IConfig[], IRunRequest>({
            query: arg => `/${arg.id}/config-details`,
            transformResponse: (x: IResponse<IConfig[]>) => x.result,
            providesTags: ['ConfigRunDetails'],
        }),
    }),
    overrideExisting: true,
});


It has two endpoints and the main difference between them is the argument ‘id’, so actually the resources are different. So the cache will be also different, one for ConfigDetails and one for ConfigRunDetails.


For the clarification on where hooks take their names, I will provide the next example:


export const {
    useFetchConfigDetailsQuery,
    useFetchConfigRunDetailsQuery,
} = configApi;


The hook name consists of the prefix ‘use’, the endpoint name fetchConfigDetails, and the postfix of the request’s type (a function that is used to create the endpoint) ‘build.query’ → useFetchConfigDetailsQuery.


And these two hooks will be used in one component:


const RowResultsDetails: React.FC<IProps> = () => {
    const { id } = useParams<IRouteParams>();

    const fetchDetails = useFetchConfigDetailsQuery(
        { },
        {
            skip: !!id,
            selectFromResult: ({ data, isFetching }) => ({
                isFetching: isFetching,
                data: selectConfigDetails(data),
            }),
        }
    );
    const fetchRunDetails = useFetchConfigRunDetailsQuery(
        { id },
        {
            skip: !id,
            selectFromResult: ({ data, isFetching }) => ({
                isFetching: isFetching,
                data: selectRunConfigDetails(data),
            }),
        }
    );

    const { data, isFetching } = id ? fetchRunDetails : fetchDetails;

    return (
        <div>
            <Section.Title>({data.length})</Section.Title>
            ...
        </div>
    );
}


As you may know, hooks in React must have the same order and can’t be inside some ‘if else’ condition. In RTK because of such constraint, there is a special property ‘skip’ direct in the hook second parameter (options). With this option, you can tell RTK to execute or not execute the hook. Additionally, for render optimization, RTK provides the possibility to use reselect library to choose the data that you need in this particular component. In other words, selectConfigDetails and selectRunConfigDetails are selectors to memoize data from the cache. If the request will be invoked and selected data is not changed, nothing will happen in the component.


The selector actually has no difference from the store selector except you don’t provide the state to it.


const emptyArray = [];

export const selectConfigDetails = createSelector(
    [
        (data?: IConfig[]) => data
    ],
    (data) => data?.filter(c => c.isAvailable) ?? emptyArray
);


An important point here is if you are using the same selector for both hooks it will cause redundant rerender. In other words ‘selector’ is unique and remembers the result. For the same selector, there will be two different values in the result. Be careful to not rerender the component frequently. As well if you prefer to use the useMemo hook to memoize data, in this scenario there needs to be two different useMemo usages.