Skip to content

Instantly share code, notes, and snippets.

@whoisYeshua
Last active November 30, 2025 07:15
Show Gist options
  • Select an option

  • Save whoisYeshua/1f8b63245fad36a13bd791daa839326e to your computer and use it in GitHub Desktop.

Select an option

Save whoisYeshua/1f8b63245fad36a13bd791daa839326e to your computer and use it in GitHub Desktop.
@reduxjs/toolkit/query example
import { createAppSelector } from '$store/utils';
import { charactersTransaction } from './transaction';
const transactionSelector = charactersTransaction.endpoints.getCharacters.select();
const charactersResult = createAppSelector(
transactionSelector,
(result: {
data?: { results?: Array<{ id: number; name: string; status: string; gender: string }> };
}) => result.data?.results
);
export const deadMalesCharacters = createAppSelector(
charactersResult,
(characters: Array<{ id: number; name: string; status: string; gender: string }> | undefined) =>
characters?.filter(
(character: { id: number; name: string; status: string; gender: string }) =>
character.status === 'Dead' && character.gender === 'Male'
)
);
import { charactersResponseSchema } from '$models/characters'
import { transactions } from '$store/transactions'
export const charactersTransaction = transactions.injectEndpoints({
endpoints: build => ({
getCharacters: build.query({
query: (_: void) => '/character',
responseSchema: charactersResponseSchema,
}),
}),
})
import { Box, Heading, Spinner, Alert, VStack, HStack } from '@chakra-ui/react'
import { useAppSelector } from '../../../store/utils'
import { charactersTransaction, charactersSelector } from './store'
export const Characters = () => {
const { data, isLoading, isError } = charactersTransaction.useGetCharactersQuery()
const deadMalesCharacters = useAppSelector(charactersSelector.deadMalesCharacters)
if (isLoading)
return (
<Box textAlign="center" py={4}>
<Spinner size="lg" />
<Box mt={2}>Loading characters...</Box>
</Box>
)
if (isError)
return (
<Alert.Root status="error">
<Alert.Indicator />
<Alert.Content>
<Alert.Title>Error!</Alert.Title>
<Alert.Description>Error loading characters</Alert.Description>
</Alert.Content>
</Alert.Root>
)
return (
<HStack align="start">
<VStack gap={2} mb={4} align="stretch">
<Heading as="h2" size="md" mb={3}>
Characters
</Heading>
{data?.results.slice(0, 10).map(character => (
<Box key={character.id} p={2} border="1px solid" borderColor="gray.600" borderRadius="md">
{character.name}
</Box>
))}
</VStack>
<VStack gap={2} align="stretch">
<Heading as="h2" size="md" mb={3}>
Dead Males Characters
</Heading>
{deadMalesCharacters?.map((character: { id: number; name: string }) => (
<Box key={character.id} p={2} border="1px solid" borderColor="gray.600" borderRadius="md">
{character.name}
</Box>
))}
</VStack>
</HStack>
)
}
import { episodesResponseSchema } from '$models/episodes'
import { transactions } from '$store/transactions'
export const episodesTransaction = transactions.injectEndpoints({
endpoints: (build) => ({
getEpisodes: build.query({
query: (_: void) => `/episode`,
responseSchema: episodesResponseSchema
})
})
})
import { Alert, Box, Heading, Spinner, VStack } from '@chakra-ui/react';
import { episodesTransaction } from './transaction';
export const Episodes = () => {
const { isLoading, isError, data } = episodesTransaction.useGetEpisodesQuery();
if (isLoading)
return (
<Box textAlign="center" py={4}>
<Spinner size="lg" />
<Box mt={2}>Loading episodes...</Box>
</Box>
);
if (isError)
return (
<Alert.Root status="error">
<Alert.Indicator />
<Alert.Content>
<Alert.Title>Error!</Alert.Title>
<Alert.Description>Error loading episodes</Alert.Description>
</Alert.Content>
</Alert.Root>
);
return (
<Box>
<Heading as="h2" size="md" mb={3}>
Episodes
</Heading>
<VStack gap={2} align="stretch">
{data?.results.slice(0, 10).map((episode) => (
<Box key={episode.id} p={2} border="1px solid" borderColor="gray.600" borderRadius="md">
{episode.episode} — {episode.name}
</Box>
))}
</VStack>
</Box>
);
};
import { configureStore } from '@reduxjs/toolkit'
import { setupListeners } from '@reduxjs/toolkit/query/react'
import { router } from '$router'
import { transactions } from './transactions'
export const extraArgument = { router }
export const store = configureStore({
reducer: {
// Add the generated reducer as a specific top-level slice
[transactions.reducerPath]: transactions.reducer,
},
// Adding the api middleware enables caching, invalidation, polling,
// and other useful features of `rtk-query`.
middleware: getDefaultMiddleware =>
getDefaultMiddleware({ serializableCheck: false, thunk: { extraArgument } }).concat(
transactions.middleware
),
})
// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch)
import { createApi, fetchBaseQuery, type QuerySubState } from '@reduxjs/toolkit/query/react'
// initialize an empty api service that we'll inject endpoints into later as needed
export const transactions = createApi({
reducerPath: 'transactions',
baseQuery: fetchBaseQuery({
baseUrl: 'https://rickandmortyapi.com/api',
prepareHeaders: (headers, api) => {
console.log(api)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Endpoints = Record<string, { select: () => (state: any) => QuerySubState<any, any> }>
const currentEndpointSelector = (transactions.endpoints as Endpoints)[api.endpoint].select()
if (currentEndpointSelector) {
const currentEndpoint = currentEndpointSelector(api.getState())
const requestId = currentEndpoint?.requestId
if (requestId) {
headers.set('Rest-Id', requestId)
}
}
return headers
},
}),
endpoints: () => ({}),
})
import { createAsyncThunk } from '@reduxjs/toolkit'
import { useDispatch, useSelector } from 'react-redux'
import { createSelector } from 'reselect'
import type { extraArgument, store } from './store'
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export type ReduxExtraArgument = typeof extraArgument
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
export const useAppSelector = useSelector.withTypes<RootState>()
export const createAppSelector = createSelector.withTypes<RootState>()
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState
dispatch: AppDispatch
extra: ReduxExtraArgument
}>()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment