T
TanStack3y ago
unwilling-turquoise

Custom hook typing overload

Hello ! I am trying to implement the great example of tkdodo for react-router/react-query but unlike him here : https://tkdodo.eu/blog/react-query-meets-react-router#:~:text=the%20loader%20function%3A-,initial%2Ddata,-Copy , I want to use my custom hook. Since then, I have the ts error : charactersQuyery.data is possibly "undefined". I guess I have to implement some overload for my custom hook but I don't know how to write that down. Here is my implementation, can someone help me on that please. Thx Characters.tsx
export default function Characters() {
const initialData = useLoaderData() as Awaited<
ReturnType<ReturnType<typeof charactersLoader>>
>;
const charactersQuery = useCharacters({
initialData,
});

if (
charactersQuery.error instanceof Error
) {
return <div>Error: {charactersQuery.error.message}</div>;
}

return (
<div>
<h1>Characters</h1>
<Link to="/">Home</Link>

<ul>
{charactersQuery.data.results.map((character) => (
// error is happening above
<li key={character.id}>{character.name}</li>
))}
</ul>
</div>
);
}
export default function Characters() {
const initialData = useLoaderData() as Awaited<
ReturnType<ReturnType<typeof charactersLoader>>
>;
const charactersQuery = useCharacters({
initialData,
});

if (
charactersQuery.error instanceof Error
) {
return <div>Error: {charactersQuery.error.message}</div>;
}

return (
<div>
<h1>Characters</h1>
<Link to="/">Home</Link>

<ul>
{charactersQuery.data.results.map((character) => (
// error is happening above
<li key={character.id}>{character.name}</li>
))}
</ul>
</div>
);
}
api/characters.ts
export const charactersQuery = () => ({
queryKey: ['characters'],
queryFn: getCharacters,
});

export function useCharacters({
initialData,
}: {
initialData?: z.infer<typeof characterSchema>;
}) {
return useQuery({ ...charactersQuery(), initialData });
}
export const charactersQuery = () => ({
queryKey: ['characters'],
queryFn: getCharacters,
});

export function useCharacters({
initialData,
}: {
initialData?: z.infer<typeof characterSchema>;
}) {
return useQuery({ ...charactersQuery(), initialData });
}
React Query meets React Router
React Query and React Router are a match made in heaven.
1 Reply
unwilling-turquoise
unwilling-turquoiseOP3y ago
Found it
import { DefinedUseQueryResult, useQuery } from '@tanstack/react-query';
import { z } from 'zod';

const characterSchema = z.object({
id: z.number(),
name: z.string(),
status: z.enum(['Alive', 'Dead', 'unknown']),
species: z.string(),
type: z.string().nullable(),
});

const charactersResponseSchema = z.object({
info: z.object({
count: z.number(),
pages: z.number(),
next: z.string().nullable(),
prev: z.string().nullable(),
}),
results: z.array(characterSchema),
});

export type Character = z.infer<typeof characterSchema>;
export type CharactersReponse = z.infer<typeof charactersResponseSchema>;

const getCharacters = async () => {
const response = await fetch('https://rickandmortyapi.com/api/character');

const charactersResponse = charactersResponseSchema.parse(
await response.json(),
);

return charactersResponse;
};

export const charactersQuery = () => ({
queryKey: ['characters'],
queryFn: getCharacters,
});

export function useCharacters(opt: {
initialData: CharactersReponse;
}): DefinedUseQueryResult<CharactersReponse>;
export function useCharacters(opt: {
initialData?: undefined;
}): DefinedUseQueryResult<undefined>;
export function useCharacters({
initialData,
}: {
initialData?: CharactersReponse;
}) {
return useQuery({ ...charactersQuery(), initialData });
}
import { DefinedUseQueryResult, useQuery } from '@tanstack/react-query';
import { z } from 'zod';

const characterSchema = z.object({
id: z.number(),
name: z.string(),
status: z.enum(['Alive', 'Dead', 'unknown']),
species: z.string(),
type: z.string().nullable(),
});

const charactersResponseSchema = z.object({
info: z.object({
count: z.number(),
pages: z.number(),
next: z.string().nullable(),
prev: z.string().nullable(),
}),
results: z.array(characterSchema),
});

export type Character = z.infer<typeof characterSchema>;
export type CharactersReponse = z.infer<typeof charactersResponseSchema>;

const getCharacters = async () => {
const response = await fetch('https://rickandmortyapi.com/api/character');

const charactersResponse = charactersResponseSchema.parse(
await response.json(),
);

return charactersResponse;
};

export const charactersQuery = () => ({
queryKey: ['characters'],
queryFn: getCharacters,
});

export function useCharacters(opt: {
initialData: CharactersReponse;
}): DefinedUseQueryResult<CharactersReponse>;
export function useCharacters(opt: {
initialData?: undefined;
}): DefinedUseQueryResult<undefined>;
export function useCharacters({
initialData,
}: {
initialData?: CharactersReponse;
}) {
return useQuery({ ...charactersQuery(), initialData });
}

Did you find this page helpful?