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:
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.
export function User() {
const { user } = useLoaderData<LoaderData>();
return <p>{user.username}</p>;
}
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.
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]
);
};
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]
);
};
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.
