According to the , “ is a powerful data fetching and caching tool. It is designed to simplify common cases for loading data in a web application, .” Redux Toolkit RTK Query eliminating the need to hand-write data fetching & caching logic yourself With the help of RTK Query, you can significantly decrease the number of actions, reducers, and side effects. Right out of the box, there is the possibility to query every N seconds, auto cache, and send repeated requests in the case when cache became invalid. It’s definitely a cool library however it’s sadly not applicable to all of the use cases possible with an API. This is because it’s managing both the state and all mutations. For example, I once tried to use it to query some table data and change it -- in other words, basic CRUD operations. I was faced, however, with an issue when I wanted to add additional data to objects and update that data calling the API. without For example, if I want to handle a few basic requests via RTK Query and not include manage state, or to not write reducers logic, etc. For better code readability, I realized it’s possible to use the and created : API splitting approach baseApi export const baseApi = createApi({ reducerPath: 'baseApi', tagTypes: ['Notifications'], baseQuery: fetchBaseQuery({ baseUrl: getBaseUrl() }), endpoints: () => ({}), }); For the API, which I need to query, I then wrote : notificationsApi const notificationsApi = baseApi.injectEndpoints({ endpoints: build => ({ fetch: build.query<INotification[], INotificationBaseRequest>({ query: buildUrl, transformResponse: (x: IResponse<INotification[]>) => x.result, providesTags: ['Notifications'], }), remove: build.mutation<void, IRemoveNotificationRequest>({ query: arg => ({ url: buildUrl(arg), method: 'DELETE', }), invalidatesTags: (_: any, id: any) => [{ type: 'Notifications', id }], }), removeAll: build.mutation<void, INotificationBaseRequest>({ query: arg => ({ url: buildUrl(arg), method: 'DELETE', }), invalidatesTags: ['Notifications'], }), }), }); export const { useFetchQuery, useRemoveMutation, useRemoveAllMutation } = notificationsApi; Where is a simple function that combines URL arguments in one string, such as notification ID to buildUrl ‘baseUrl/notifications/{id}’. In the section I introduced three operations that I needed to use. endpoints The operation retrieves data by . fetch GET request The data looks like an array: [ { id: number; name: string; message: string; }, ... ] This array I can easily get in component via the hook. useFecthQuery const { data, isLoading } = useFetchQuery({ ...params }); Here, I don’t need to implement logic that actually describes the status of the HTTP request. isLoading It is already there in the hook. However, please be careful, the is for the first query only. isLoading For common cases is more appropriate. The hook will automatically request data (in case if there is no data or the cache is invalid). isFetching More about cache. The operation is used to send a DELETE request and remove one notification. remove The is there to remove only one entry from the array by id. invalidatesTags const [remove] = useRemoveMutation(); Use it without dispatch, just as it is. You can call on a button click or another user action. As well as the operation. remove removeAll const [removeAll, { isLoading: isDeleting }] = useRemoveAllMutation(); Also in such an operation, there is a possibility to use properties to understand the status of HTTP requests. The main difference between and is that in removeAll I remove the whole cache. remove removeAll That’s pretty much it, all data stored, actions automatically generated. No need to manage it manually… or so I thought. But I understood that I want to extend objects in the array, to have an additional property: . It can not be done easily - just changing the current state of cached objects. isCollapsed The RTK Query manages the state by itself and I can’t create another action to handle such behavior. However, I found two ways how to solve it! First of all, I could create my own state , where there will be additional data stored, of course, I need to or and add more actions. NotificationState createSlice createReducer Or I will not use RTK Query, and all will be done via where objects will be extended with the required fields. createSlice The general takeaway here is, if for some reason you need to extend objects, or in your API, objects come in different schemas and you want to store it all in one place, it is better to manage the state by yourself from the begining.