Libraries like SWR or React Query have automatic data revalidation built in, but what about Remix?
Remix has a great built-in utility to fetch data (loader
and useLoaderData
). It automatically revalidates data on all actions.
Let’s see how you can add more ways to revalidate your data such as interval/focus based data revalidation:
Create a loader to fetch your data
type LoaderData= {
user: User;
};
export const loader: LoaderFunction = async () => {
const res = await fetch("/api/user/me");
const data = await res.json();
return json<LoaderData>({ user: data });
};
This will make remix fetch the data whenever the route loads.
Make sure loader is exported from the same file your route is in.
Use the loader in your component
export function User() {
const { user } = useLoaderData<LoaderData>();
return <p>{user.username}</p>;
}
Create a revalidate hook
useRevalidate
can be use to trigger data refresh. It triggers a navigation event to the current route which triggers Remix to call loader of the route.
import { useCallback } from "react";
import { useNavigate } from "@remix-run/react";
export const useRevalidate = () => {
// We get the navigate function from React Rotuer
let navigate = useNavigate();
// And return a function which will navigate to `.` (same URL) and replace it
return useCallback(
function revalidate() {
navigate(".", { replace: true });
},
[navigate]
);
};
replace: true
prevents it from creating duplicate routes in browser history stack.
Create a hook to revalidate on focus
import { useEffect } from "react";
export const useRevalidateOnFocus = ({
enabled = false,
}: {
enabled?: boolean;
}) => {
let revalidate = useRevalidate();
useEffect(
function revalidateOnFocus() {
if (!enabled) return;
function onFocus() {
revalidate();
}
window.addEventListener("focus", onFocus);
return () => window.removeEventListener("focus", onFocus);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[revalidate]
);
useEffect(
function revalidateOnVisibilityChange() {
if (!enabled) return;
function onVisibilityChange() {
revalidate();
}
window.addEventListener("visibilitychange", onVisibilityChange);
return () =>
window.removeEventListener("visibilitychange", onVisibilityChange);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[revalidate]
);
};
Create a hook to revalidate based on interval
import { useEffect } from "react";
export const useRevalidateOnInterval = ({
enabled = false,
interval = 1000,
}: {
enabled?: boolean;
interval?: number;
}) => {
let revalidate = useRevalidate();
useEffect(
function revalidateOnInterval() {
if (!enabled) return;
let intervalId = setInterval(revalidate, interval);
return () => clearInterval(intervalId);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[revalidate]
);
};
Use the hooks we created
Now just call the hook in route where loader is in.
export function User() {
useRevalidateOnInterval({
enabled: true,
interval: 5 * 1000,
});
const { user } = useLoaderData<LoaderData>();
return <p>{user.username}</p>;
}
in the above example it would re-validate user every 5 seconds.
Top comments (0)