Read the blog post about building this library: https://blog.devesh.tech/post/creating-our-own-swr
- Reactivity Baked In
- Caching of data cross-app
- Realtime updates cross-app
- Options to revalidate data on mount and on focus of browser
- Fallback Data support for server-side rendering
- Conditional Fetching
- Global Config Provider
- Global Config Retreival Hook
- Error Retries
useFetch(
key: string | null,
options?: {
revalidateOnMount?: boolean,
revalidateOnFocus?: boolean,
dedupingInterval?: milliseconds,
fallbackData?: any,
fetcher?: (key: string) => Promise<any>;
dedupingInterval?: number;
onSuccess?: (data: any, key: string | null, config: options) => any;
onError?: (error: Error, key: string | null, config: options) => any;
}
)
import useFetch from "use-fetch";
const Component = () => {
const { data, error, isValidating, revalidate } = useFetch("/api/v1/data");
if (isValidating) return "Loading";
if (error) return `Error: ${error.message}`;
return (
<>
Data: {data}
<Button onClick={() => revalidate()}>Refetch</Button>
</>
);
};
Use
revalidate(newDate?: any, refetchFromAPI?: boolean)
Every revalidation fetches data from the API in the background, you can disable that by passing false as the second argument.
const { data, revalidate } = useFetch("/api/v1/data");
revalidate(); // Just make the API call again and populate the cache
revalidate(localData); // Set the data and cache to localData, but make API Call in the background to update with real data. Like Optimistic Rendering
revalidate(localData, false); // Set the data and cache to localData and do not make an API Call in the background
For multiple useFetch hooks, it's cumbersome to pass config/options to each of them separately. Hence, use-fetch
comes with a FetchProvider
context provider to share the config among all hooks.
import { FetchProvider } from "use-fetch";
return (
<FetchProvider
value={{
revalidateOnMount: boolean,
revalidateOnFocus: boolean,
dedupingInterval: number(milliseconds),
fallback: Record<string, any>,
fetcher: (key) => any,
dedupingInterval: number,
}}
>
... All components containing useFetch hooks
</FetchProvider>
);
Retrieve the global config for your fetcher hooks using the useFetchConfig
hook.
import { useFetchConfig } from "use-fetch";
const {
revalidate,
fetcher,
fallback,
cache,
dedupingInterval,
revalidateOnMount,
revalidateOnFocus,
} = useFetchConfig();
// This revalidate works differently.
// It takes 3 args instead of 2.
revalidate(
keyOfFetcherYouWantToUpdate,
localData?,
refetchFromAPI?
);
const key = "/api/v1/data";
const Page = ({ fallbackData }) => {
const { data } = useFetch(key, { fallbackData: fallbackData[key] });
// or
return (
<FetchProvider value={{ fallback: fallbackData }}>
{/*
All hooks internally would either use their on fallback data or the fallback data from the above provider corresponding to their keys.
*/}
...
</FetchProvider>
);
};
export const getStaticProps = async () => {
const data = await fetchData(key);
return { props: { fallbackData: { [key]: data } } };
};
export default Page;
Use the onSuccess
and onError
handlers for handling the completion and error-ing of the API Calls.
useFetch("/api/v1/data", {
onSuccess: (data, key, config) => {
console.log("Data fetched:", data);
},
onError: (error, key, config) => {
console.log("Error while fetching:", error);
},
});
Or you could use reactivity to your advantage.
useEffect(() => {
if (data !== undefined) console.log("Data fetched:", data);
}, [data]);
useEffect(() => {
if (error !== undefined) console.log("Error while fetching:", error);
}, [error]);