new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Redux Toolkit · RTK Query · all subjects

cache-management

25 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

API utilities location

The API slice object includes various utilities that can be used for cache management, such as implementing optimistic updates and server side rendering. These are included as api.util inside the API object.

API slice utils field contents

The utils field includes utility functions to manage the cache: updateQueryData (UpdateQueryDataThunk), patchQueryData (PatchQueryDataThunk), prefetch (PrefetchThunk), invalidateTags, selectInvalidatedBy, selectCachedArgsForQuery, resetApiState, getRunningQueryThunk, getRunningMutationThunk, getRunningQueriesThunk, and getRunningMutationsThunk.

RTK Query deliberately does not deduplicate cache across requests

RTK Query deliberately does not implement a cache that deduplicates identical items across multiple requests. Instead, it simply refetches data when it is invalidated, which works well in most cases and is easier to understand.

RTK Query cache key strategy

RTK Query caches data by endpoint plus serialized arguments, unlike React Query which uses user-defined query keys or Apollo/Urql which cache by type/id.

RTK Query supports auto garbage collection

RTK Query supports automatic garbage collection of cached data when it is no longer needed.

RTK Query does not implement normalized or de-duplicated cache

RTK Query deliberately does not implement a cache that deduplicates identical items across multiple requests. Each query result is saved independently in the cache, so if multiple endpoints return the same object, separate copies are stored. However, if all endpoints consistently provide the same tags (such as {type: 'Todo', id: 1}), then invalidating that tag will force all matching endpoints to refetch their data for consistency.

selectFromResult hook option for reading individual pieces from query result

Generated query hooks have a selectFromResult option that allows components to read individual pieces of data from a query result. For example, a TodoList component might call useTodosQuery(), and each individual TodoListItem could use the same query hook but select from the result to get the right todo object.

keepUnusedDataFor configuration example

Example showing createApi with keepUnusedDataFor: 30 at the API level (global) and keepUnusedDataFor: 5 on the getPosts endpoint (overriding the global setting).

Cache keying: queryCacheKey based on endpoint and serialized parameters

When a subscription is started, the parameters used with the endpoint are serialized and stored internally as a queryCacheKey. Any future request that produces the same queryCacheKey (called with the same parameters, factoring serialization) will be de-duped against the original and share the same data and updates. Two separate components performing the same request will use the same cached data.

Default cache serving behavior

When a request is attempted, if the data already exists in the cache, that data is served and no new request is sent to the server. If the data does not exist in the cache, a new request is sent and the returned response is stored in the cache.

Subscription reference counting and cache retention

Subscriptions are reference-counted. Additional subscriptions asking for the same endpoint plus parameters increment the reference count. As long as there is an active subscription to the data (for example, a component mounted that calls a useQuery hook), the data remains in the cache. Once the subscription is removed (for example, when the last component subscribed unmounts), after an amount of time (default 60 seconds), the data is removed from the cache.

keepUnusedDataFor configuration at API and endpoint levels

The keepUnusedDataFor property controls how long data remains in the cache after the subscriber reference count reaches zero. It accepts a number in seconds. This can be configured at the API definition level (applies globally) or on a per-endpoint basis. The per-endpoint setting overrides the API definition setting. The default value is 60 seconds.

Cache subscription lifetime with multiple components example

Example scenario with four components making queries to useGetUserQuery with different ids. ComponentOne queries id 1, ComponentTwo queries id 2, ComponentThree queries id 3, and ComponentFour also queries id 3. RTK Query makes three distinct fetches for the three unique endpoint plus parameter combinations. Query parameter 3 has two subscribers (reference count 2), while parameters 1 and 2 each have one subscriber (reference count 1). Data is kept in cache as long as at least one subscriber is active. When ComponentThree unmounts, data remains cached due to ComponentFour still being subscribed with reference count 1. When ComponentFour unmounts, reference count reaches 0 and data remains for the expiration time (default 60 seconds). If no new subscription is created before the timer expires, the cached data is removed.

Pessimistic update core concepts

The core concepts for a pessimistic update are: when you start a query or mutation, onQueryStarted will be executed; you await queryFulfilled to resolve to an object containing the transformed response from the server in the data property; you manually update the cached data by dispatching api.util.updateQueryData within onQueryStarted, using the data in the response from the server for your draft updates; you may manually create a new cache entry by dispatching api.util.upsertQueryData within onQueryStarted, using the complete object returned by backend.

Manual cache updates via thunks dispatching

RTK Query exports updateQueryData and upsertQueryData thunks attached to api.utils. Since these are thunks, they can be dispatched anywhere you have access to dispatch.

Automated re-fetching recommended over manual cache updates

For most cases, to receive up to date data after triggering a change in the backend, you should use cache tag invalidation to perform automated re-fetching. This is recommended as a preference over manual cache updates in most situations. Manual cache updates are necessary only in specific use cases such as optimistic or pessimistic updates, or modifying data as part of cache entry lifecycles.

Pessimistic update mutation example with updateQueryData

async onQueryStarted({ id, ...patch }, { dispatch, queryFulfilled }) { try { const { data: updatedPost } = await queryFulfilled const patchResult = dispatch( api.util.updateQueryData('getPost', id, (draft) => { Object.assign(draft, updatedPost) }), ) } catch {} } This example shows a pessimistic update mutation that waits for the server response before updating the cache with the updated data.

General manual cache update outside onQueryStarted

If you find yourself wanting to update cache data elsewhere in your application, you can do so anywhere you have access to the store.dispatch method, including within React components via the useDispatch hook or a typed version such as useAppDispatch for typescript users. However, you should generally avoid manually updating the cache outside of the onQueryStarted callback for a mutation without a good reason, as RTK Query is intended to be used by considering your cached data as a reflection of the server-side state.

Cached vs non-cached pagination behavior

When navigating between pages in a paginated RTK Query, the initial query shows a loading state. When moving forward to new pages, non-cached queries show a fetching indicator via the hook's isFetching property. When navigating backward to previously visited pages, cached data is served immediately without re-fetching.

Browser cache preferable to API slice persistence

Persisting API slices is generally not recommended. Instead, mechanisms like Cache-Control Headers should be used in browsers to define cache behavior. Persisting and rehydrating an API slice might leave the user with very stale data if the user has not visited the page for some time. However, in environments like Native Apps where there is no browser cache, persistence might still be a viable option.

Force refetch to skip deduplication

To skip the default behavior of reusing cached queries and force a refetch, call the refetch function returned by the query hook.

Query cache key deduplication

When a query is performed, RTK Query automatically serializes the request parameters and creates an internal queryCacheKey for the request. Any future request that produces the same queryCacheKey will be de-duped against the original and will share updates if a refetch is triggered on the query from any subscribed component.

Default behavior: no duplicate requests for same query

By default, if a component is added that makes the same query as an existing one, no request will be performed. The cached result is used instead.

Add cache subscription with initiate thunk

Cache subscriptions are added by dispatching the result of the initiate thunk action creator attached to a query endpoint. The dispatch returns a promise with data, isLoading, isSuccess, refetch, and other properties. Example: const promise = dispatch(api.endpoints.getPosts.initiate()); const { refetch } = promise; const { data, isLoading, isSuccess } = await promise;

Remove cache subscription with unsubscribe

Removing a cache subscription is necessary for RTK Query to identify that cached data is no longer required and to clean up old cache data. The result of dispatching the initiate thunk has an unsubscribe property that is a function. Calling unsubscribe() removes the corresponding cache subscription. Example: const promise = dispatch(api.endpoints.getPosts.initiate()); promise.unsubscribe();

Give your agent this brain