35
loading...
This website collects cookies to deliver better user experience
const [pokemon, setPokemon] = React.useState<Array<Pokemon>>();
const [isLoading, setIsLoading] = React.useState<boolean>(false);
const getData = () => {
setIsLoading(true);
axios
.get<PokemonList>('https://pokeapi.co/api/v2/pokemon?limit=20')
.then((res) => {
setPokemon(res.data.results);
})
.finally(() => setIsLoading(false));
};
return <button disabled={isLoading}>{isLoading ? 'loading' : 'fetch'}</button>;
toast.promise(
axios
.get<PokemonList>('https://pokeapi.co/api/v2/pokemon?limit=20')
.then((res) => {
setPokemon(res.data.results);
}),
{
loading: 'Loading...',
success: 'Data fetched successfully',
error: (err: any) =>
err?.response?.data?.msg ?? 'Something is wrong, please try again',
}
);
yarn add react-hot-toast
_app.tsx
import { AppProps } from 'next/app';
import '@/styles/globals.css';
import DismissableToast from '@/components/DismissableToast';
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<DismissableToast />
<Component {...pageProps} />
</>
);
}
export default MyApp;
DismissableToast
code from my library.React.useEffect(() => {
toast.promise(
axios
.get<PokemonList>('https://pokeapi.co/api/v2/pokemon?limit=20')
.then((res) => {
setPokemon(res.data.results);
}),
{
loading: 'Loading...',
success: 'Data fetched successfully',
error: (err: any) =>
err?.response?.data?.msg ?? 'Something is wrong, please try again',
}
);
}, []);
defaultToastMessage
, then overriding it if you need to.export const defaultToastMessage = {
loading: 'Loading...',
success: 'Data fetched successfully',
// you can type this with axios error
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error: (err: any) =>
err?.response?.data?.msg ?? 'Something is wrong, please try again',
};
toast.promise(axios, {
...defaultToastMessage,
loading: 'Override loading',
});
Now, what if we want to get that loading state to disable a button?
import { useToasterStore } from 'react-hot-toast';
/**
* Hook to get information whether something is loading
* @returns true if there is a loading toast
* @example const isLoading = useLoadingToast();
*/
export default function useLoadingToast(): boolean {
const { toasts } = useToasterStore();
const isLoading = toasts.some((toast) => toast.type === 'loading');
return isLoading;
}
const isLoading = useLoadingToast();
<button disabled={isLoading}></button>;
isLoading
state, the rest is all your creativity, you can show some skeleton, change the loading text, give loading spinners, anything you like.then
to get the value.toast.promise(
axios
.post('/user/login', data)
.then((res) => {
const { jwt: token } = res.data.data;
tempToken = token;
localStorage.setItem('token', token);
// chaining axios in 1 promise
return axios.get('/user/get-user-info');
})
.then((user) => {
const role = user.data.data.user_role;
dispatch('LOGIN', { ...user.data.data, token: tempToken });
history.replace('/');
}),
{
...defaultToastMessage,
}
);
const { data, error } = useSWR<PokemonList>(
'https://pokeapi.co/api/v2/pokemon?limit=20'
);
Hmm, this is not a promise, how do we use the toast then?
useSWR
just like the toast.promise
function.import * as React from 'react';
import toast from 'react-hot-toast';
import { SWRResponse } from 'swr';
import { defaultToastMessage } from '@/lib/helper';
import useLoadingToast from '@/hooks/useLoadingToast';
type OptionType = {
runCondition?: boolean;
loading?: string;
success?: string;
error?: string;
};
export default function useWithToast<T, E>(
swr: SWRResponse<T, E>,
{ runCondition = true, ...customMessages }: OptionType = {}
) {
const { data, error } = swr;
const toastStatus = React.useRef<string>(data ? 'done' : 'idle');
const toastMessage = {
...defaultToastMessage,
...customMessages,
};
React.useEffect(() => {
if (!runCondition) return;
// if toastStatus is done,
// then it is not the first render or the data is already cached
if (toastStatus.current === 'done') return;
if (error) {
toast.error(toastMessage.error, { id: toastStatus.current });
toastStatus.current = 'done';
} else if (data) {
toast.success(toastMessage.success, { id: toastStatus.current });
toastStatus.current = 'done';
} else {
toastStatus.current = toast.loading(toastMessage.loading);
}
return () => {
toast.dismiss(toastStatus.current);
};
}, [
data,
error,
runCondition,
toastMessage.error,
toastMessage.loading,
toastMessage.success,
]);
return { ...swr, isLoading: useLoadingToast() };
}
useLoadingToast
hooks anymoreconst { data: pokemonData, isLoading } = useWithToast(
useSWR<PokemonList>('https://pokeapi.co/api/v2/pokemon?limit=20')
);
const { data: pokemonData, isLoading } = useWithToast(
useSWR<PokemonList>('https://pokeapi.co/api/v2/pokemon?limit=20'),
{
loading: 'Override Loading',
}
);
Originally posted on my personal site, find more blog posts and code snippets library I put up for easy access on my site 🚀