Where do we get this data from?
{ "count": 1118, "next": "https://pokeapi.co/api/v2/pokemon?offset=5&limit=5", "previous": null, "results": [ { "name": "bulbasaur", "url": "https://pokeapi.co/api/v2/pokemon/1/" }, { "name": "ivysaur", "url":"https://pokeapi.co/api/v2/pokemon/2/" }, … ] }
We will use TanStack Query in this course
Configure TanStack Query
npm i @tanstack/react-query
const queryClient = new QueryClient(); function App() { /* Provide the client to your App */ return ( <QueryClientProvider client={queryClient}> <MyComponent /> </QueryClientProvider> ); }
const queryClient = new QueryClient(); function App() { /* Provide the client to your App */ return ( <QueryClientProvider client={queryClient}> <MyComponent /> </QueryClientProvider> ); }
Using Tanstack Query for data fetching
const fetcher = async (uri: string): Promise => { const response = await fetch(uri); if (!response.ok) throw new Error('Could not fetch data!'); return response.json(); }; function MyComponent() { const { data: user, isLoading, error } = useQuery(['user'], () => fetcher('/user')); return <p>{user?.name}</p>; }
const fetcher = async (uri: string): Promise => { const response = await fetch(uri); if (!response.ok) throw new Error('Could not fetch data!'); return response.json(); }; function MyComponent() { const { data: user, isLoading, error } = useQuery(['user'], () => fetcher('/user')); return <p>{user?.name}</p>; }
Clean & simple – also comes with with retry, cache & more functionality
const fetcher = async <T>(uri: string): Promise<T> => { const response = await fetch(uri); if (!response.ok) throw new Error('Could not fetch data!'); return response.json(); }; const { data: user } = useQuery(['user'], () => fetcher<User>('/user')); interface User { firstName: string, /* ... */ }
Since we are using TypeScript, we should always work with defined types—avoid any !
export async function fetcher<T>(uri: string): Promise<T> {
const response = await fetch(uri);
if (!response.ok) throw new Error('Could not fetch data!');
return response.json();
};
const queryClient = new QueryClient(); function App() { return ( <QueryClientProvider client={queryClient}> <MyComponent /> </QueryClientProvider> ); }
https://pokeapi.co/api/v2/pokemon?limit=5
https://pokeapi.co/api/v2/pokemon/bulbasaur/
ListPage.tsx
function List() { const { data, isLoading, isError } = useQuery( ['pokelist'], () => fetcher<PokemonResultDto>( 'https://pokeapi.co/api/v2/pokemon?limit=1000' ) ); if (isLoading) return <div>LOADING</div>; if (isError) return <div>ERROR while loading data</div>; return <PokeList pokemons={data.results} />; }
function List() { const { data, isLoading, isError } = useQuery( ['pokelist'], () => fetcher<PokemonResultDto>( 'https://pokeapi.co/api/v2/pokemon?limit=1000' ) ); if (isLoading) return <div>LOADING</div>; if (isError) return <div>ERROR while loading data</div>; return <PokeList pokemons={data.results} />; }
function List() { const { data, isLoading, isError } = useQuery( ['pokelist'], () => fetcher<PokemonResultDto>( 'https://pokeapi.co/api/v2/pokemon?limit=1000' ) ); if (isLoading) return <div>LOADING</div>; if (isError) return <div>ERROR while loading data</div>; return <PokeList pokemons={data.results} />; }
DetailPage.tsx
function DetailPage() { const { pokemonName } = useParams<"pokemonName">(); const uri = `https://pokeapi.co/api/v2/pokemon/${pokemonName}`; const { data, isLoading, isError } = useQuery( ["pokemon", "detail", pokemonName], () => fetcher<PokemonDetailDto>(uri) ); if (isLoading) return <div>LOADING</div>; if (isError) return <div>ERROR while loading data</div>; return ( <div> <span>{pokemonName}</span> <img src={data.sprites.front_shiny} alt={pokemonName} /> </div> ); }
function DetailPage() { const { pokemonName } = useParams<"pokemonName">(); const uri = `https://pokeapi.co/api/v2/pokemon/${pokemonName}`; const { data, isLoading, isError } = useQuery( ["pokemon", "detail", pokemonName], () => fetcher<PokemonDetailDto>(uri) ); if (isLoading) return <div>LOADING</div>; if (isError) return <div>ERROR while loading data</div>; return ( <div> <span>{pokemonName}</span> <img src={data.sprites.front_shiny} alt={pokemonName} /> </div> ); }
Why doesnt useQuery fit?
function AddPokemon() { const { mutate, isLoading: isMutationLoading, isError: isMutationerror } = useMutation((name: string) => fetcher<PokemonDetailDto>("https://pokeapi.co/api/v2/pokemon?limit=1000", { method: "POST", body: JSON.stringify({ name }), }) ); return <button onClick={() => mutate("zuehlkemon")} />; }
function AddPokemon() { const { mutate, isLoading: isMutationLoading, isError: isMutationerror } = useMutation((name: string) => fetcher<PokemonDetailDto>("https://pokeapi.co/api/v2/pokemon?limit=1000", { method: "POST", body: JSON.stringify({ name }), }) ); return <button onClick={() => mutate("zuehlkemon")} />; }
function AddPokemon() { const { mutate, isLoading: isMutationLoading, isError: isMutationerror } = useMutation((name: string) => fetcher<PokemonDetailDto>("https://pokeapi.co/api/v2/pokemon?limit=1000", { method: "POST", body: JSON.stringify({ name }), }) ); return <button onClick={() => mutate("zuehlkemon")} />; }
notice how there is no query key and no data
const { data, isLoading, isError } = useQuery(['pokemonList'], () => fetcher<PokemonResultDto>('https://pokeapi.co/api/v2/pokemon?limit=1000') ); const queryClient = useQueryClient(); const { mutate } = useMutation( (name: string) => fetcher<PokemonDetailDto>('https://pokeapi.co/api/v2/pokemon?limit=1000', { method: 'POST', body: JSON.stringify({ name }), }), { onSuccess: (mutateResult, mutateParams) => { const newPokeList = data ? [...data.results, mutateResult] : [mutateResult]; queryClient.setQueryData(['pokemonList'], { results: newPokeList }); }, } );
const { data, isLoading, isError } = useQuery(['pokemonList'], () => fetcher<PokemonResultDto>('https://pokeapi.co/api/v2/pokemon?limit=1000') ); const queryClient = useQueryClient(); const { mutate } = useMutation( (name: string) => fetcher<PokemonDetailDto>('https://pokeapi.co/api/v2/pokemon?limit=1000', { method: 'POST', body: JSON.stringify({ name }), }), { onSuccess: (mutateResult, mutateParams) => { const newPokeList = data ? [...data.results, mutateResult] : [mutateResult]; queryClient.setQueryData(['pokemonList'], { results: newPokeList }); }, } );
const { data, isLoading, isError } = useQuery(['pokemonList'], () => fetcher<PokemonResultDto>('https://pokeapi.co/api/v2/pokemon?limit=1000') ); const queryClient = useQueryClient(); const { mutate } = useMutation( (name: string) => fetcher<PokemonDetailDto>('https://pokeapi.co/api/v2/pokemon?limit=1000', { method: 'POST', body: JSON.stringify({ name }), }), { onSuccess: (mutateResult, mutateParams) => { const newPokeList = data ? [...data.results, mutateResult] : [mutateResult]; queryClient.setQueryData(['pokemonList'], { results: newPokeList }); }, } );
const { data, isLoading, isError } = useQuery(['pokemonList'], () => fetcher<PokemonResultDto>('https://pokeapi.co/api/v2/pokemon?limit=1000') ); const queryClient = useQueryClient(); const { mutate } = useMutation( (name: string) => fetcher<PokemonDetailDto>('https://pokeapi.co/api/v2/pokemon?limit=1000', { method: 'POST', body: JSON.stringify({ name }), }), { onSuccess: (mutateResult, mutateParams) => { const newPokeList = data ? [...data.results, mutateResult] : [mutateResult]; queryClient.setQueryData(['pokemonList'], { results: newPokeList }); }, } );
We learned…