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

createapi/lifecycle

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

onQueryStarted example for dispatch side effects

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query' import { messageCreated } from './notificationsSlice' export interface Post { id: number name: string } const api = createApi({ baseQuery: fetchBaseQuery({ baseUrl: '/' }), endpoints: (build) => ({ getPost: build.query<Post, number>({ query: (id) => `post/${id}`, async onQueryStarted(id, { dispatch, queryFulfilled }) { dispatch(messageCreated('Fetching post...')) try { const { data } = await queryFulfilled dispatch(messageCreated('Post received!')) } catch (err) { dispatch(messageCreated('Error fetching post!')) } }, }), }), })

onQueryStarted lifecycle hook for queries and mutations

onQueryStarted(arg, {dispatch, getState, extra, requestId, queryFulfilled, getCacheEntry, updateCachedData}) is called when you start each individual query or mutation. The function receives a lifecycle API with queryFulfilled (a Promise that resolves with data and meta, or rejects with error), getCacheEntry (a function to get current cache entry value), and updateCachedData (available for query endpoints only, accepts a recipe callback using immer to update cache).

onCacheEntryAdded lifecycle hook

onCacheEntryAdded(arg, {dispatch, getState, extra, requestId, cacheEntryRemoved, cacheDataLoaded, getCacheEntry, updateCachedData}) is called when a new cache entry is added (when a new subscription for endpoint + query parameters combination is created). It provides cacheEntryRemoved (Promise that resolves when cache entry is removed), cacheDataLoaded (Promise that resolves with first value for cache key, or rejects if entry removed before value resolved), and updateCachedData (for query endpoints only).

queryResultPatched reducer

The queryResultPatched reducer patches a specific cacheKey's result in the querySlice.

queryThunk.pending reducer behavior

The queryThunk.pending extraReducer initially sets QueryStatus to uninitialized, then updates it to pending, generates a requestId, stores originalArgs, and stores startedTimeStamp.

queryThunk.fulfilled cache update

When queryThunk.fulfilled is handled without merge, it updates the cache data, creates a fulfilledTimeStamp, and deletes the substate error.

queryThunk.rejected condition handling

The queryThunk.rejected extraReducer uses condition() from queryThunk and does nothing if the rejection is a result of condition(), which indicates a thunk is already running. Otherwise it sets substate.error and changes status to rejected.

mutationThunk.pending reducer behavior

The mutationThunk.pending extraReducer exits if track is set to false, otherwise it updates the appropriate cacheKey with requestId, pending status, and startedTimeStamp.

mutationThunk.fulfilled reducer behavior

The mutationThunk.fulfilled extraReducer exits if track is set to false, otherwise it sets data from payload and fulfilledTimeStamp.

mutationThunk.rejected reducer behavior

The mutationThunk.rejected extraReducer exits if track is set to false, otherwise it sets error and status to rejected.

hasRehydrationInfo handler for mutation slice

The hasRehydrationInfo extraReducer iterates through and resets entries for all fulfilled or rejected status in the mutationSlice.

subscriptionSlice reducers

The subscriptionSlice contains reducers for updateSubscriptionOptions, unsubscribeQueryResult, internal_getRTKQSubscriptions, and subscriptionsUpdated. The subscriptionsUpdated reducer applies patches to the state from the payload.

middlewareRegistered reducer

The middlewareRegistered reducer in configSlice toggles whether the middleware is registered or if there is a conflict.

configSlice online/offline handlers

The configSlice has onOnline and onOffline extraReducers that manage state.online in response to listenerMiddleware.

configSlice focus handlers

The configSlice has onFocus and onFocusLost extraReducers that manage state.focused in response to listenerMiddleware.

getMutationSubstateIfExists utility function

The getMutationSubstateIfExists utility function is the same as the query version except it uses the id instead of the queryCacheKey and uses getMutationCacheKey to determine the cachekey. It takes the state, id, and an update function, and executes the update function on the mutation substate if it exists.

updateQuerySubstateIfExists utility function

The updateQuerySubstateIfExists utility function takes the api/endpoint state, queryCacheKey, and an update function. It determines the substate by accessing the queryCacheKey value in the state, and if the substate exists, executes the update function on it.

removeMutationResult reducer

The removeMutationResult reducer calls getMutationCacheKey from payload and deletes the corresponding draft entry if the cacheKey exists.

removeQueryResult reducer

The removeQueryResult reducer deletes a specific cacheKey's stored result from the querySlice.

cacheLifecycle uses queryThunk matching for cache differentiation

The cacheLifecycle middleware uses queryThunk matching to differentiate between mutation cache and query cache handling.

queryLifecycle leverages queryThunk for query specific traits

The queryLifecycle middleware leverages the createAsyncThunk pending/fulfilled/rejected actions from queryThunk to extend the lifecycle with query specific traits. It also uses queryThunk to handle onQueryStarted.

buildSlice uses queryThunk extra reducers

The query endpoint in buildSlice is built almost entirely off of extraReducers matching queryThunk pending/fulfilled/rejected actions, updating the querySubstate and metadata accordingly. buildSlice also matches resolved queryThunks (either rejected or fulfilled) to update providedTags.

Error catching middleware example with isRejectedWithValue

This example demonstrates a middleware that catches rejected RTK Query actions using isRejectedWithValue from redux-toolkit. It logs a warning and shows a toast notification with the error message, extracting the message from action.error.data or action.error.message depending on the error structure.

Handling errors at macro level with Redux middleware

RTK Query is built on Redux and Redux-Toolkit, so you can easily add middleware to the store to manage errors at a macro level, such as showing generic toast notifications for any async error.

RTK Query uses createAsyncThunk for middleware matchers

RTK Query uses createAsyncThunk from redux-toolkit under the hood, which allows the use of Redux Toolkit action matching utilities like isRejectedWithValue in middleware.

onCacheEntryAdded callback parameters

The onCacheEntryAdded lifecycle callback receives two arguments: the arg that was passed to the subscription, and an options object containing lifecycle promises and utility functions. The options object includes cacheDataLoaded, updateCachedData, and cacheEntryRemoved.

Streaming updates purpose and WebSocket connection

RTK Query enables streaming updates for persistent queries to establish ongoing connections to the server, typically using WebSockets, and apply updates to cached data as additional information is received. This allows the API to receive real-time updates such as new entries being created or important properties being updated.

When to use streaming updates over polling

Streaming updates are particularly useful for scenarios involving small, frequent changes to large objects (where repeatedly polling would be inefficient) and external event-driven updates (where real-time updates are expected and polling would cause periods of stale data). Use cases include GraphQL subscriptions, real-time chat applications, real-time multiplayer games, and collaborative document editing with multiple concurrent users.

Streaming updates with transformed response using EntityAdapter

Example: A getMessages query can use transformResponse to transform the response data shape using createEntityAdapter.addMany to convert from array format to EntityState with ids and entities object. In the onCacheEntryAdded callback, use messagesAdapter.upsertOne(draft, data) within updateCachedData to add streamed updates while maintaining the normalized state structure.

Streaming updates respect transformed cache data shape

Updates to cached data within onCacheEntryAdded must respect the transformed data shape that will be present for the cached data. When using transformResponse to normalize data with EntityAdapter, the streaming update logic must use the adapter methods like upsertOne to maintain the normalized state structure.

onCacheEntryAdded typical execution sequence

The typical sequence is to await cacheDataLoaded to determine when the first data has been fetched, then use updateCachedData utility to apply streaming updates as messages are received. updateCachedData is an Immer-powered callback that receives a draft of the current cache value which can be mutated. Finally, await cacheEntryRemoved to know when to clean up server connections.

Streaming updates with WebSocket example

Example: A query endpoint named getMessages that accepts a Channel argument and returns Message[]. The onCacheEntryAdded callback creates a WebSocket connection, waits for cacheDataLoaded, adds a message event listener to the WebSocket, and in the listener parses incoming data, validates it with isMessage and checks it matches the arg channel, then calls updateCachedData to push the message to the draft array. When cacheEntryRemoved resolves, the WebSocket is closed for cleanup.

Cache entry lifecycle and refresh behavior

When a query for a cache entry runs, it will overwrite the whole cache entry, and streaming update listeners will continue to work on the updated data. When there are no more active subscriptions to the data, the cacheEntryRemoved promise resolves and RTK Query removes the associated data from the cache.

onQueryStarted lifecycle callback signature

onQueryStarted is an async callback with signature (arg, QueryLifecycleApi) where QueryLifecycleApi is an object containing: dispatch, getState, extra, requestId, queryFulfilled, getCacheEntry, and updateCachedData.

onCacheEntryAdded lifecycle callback signature

onCacheEntryAdded is an async callback with signature (arg, QueryCacheLifecycleApi) where QueryCacheLifecycleApi is an object containing: dispatch, getState, extra, requestId, cacheEntryRemoved, cacheDataLoaded, getCacheEntry, and updateCachedData.

Give your agent this brain