Call Remix loaders on demand with useRevalidate
17th January 2023Remix loaders are functions that run server-side to fetch data before rendering a route. This is a super convenient way to do server-ish things that the client can’t be trusted with — auth, connecting to a db etc.
export const loader = async () => {
const { data } = await supabase.from("posts").select();
return { data };
};
We can then access this data in our component with the useLoaderData
hook.
export default function Index() {
const { data } = useLoaderData();
return <pre>{JSON.strinigfy(data, null, 2)}</pre>;
}
This works perfectly for the first load of the page, and ensures that the data is available for the first SSR render, but there hasn’t really been an easy way to programatically call these loaders when something happens client-side — signing in with Supabase, for example.
Until now!
As of version 1.10.0, Remix exposes a new useRevalidate
hook which does exactly this.
import { useRevalidator } from "@remix-run/react";
export default function Index() {
const revalidator = useRevalidator();
const handleLoad = () => {
revalidator.revalidate();
};
return <button onClick={handleLoad}>Load</button>;
}
The cool thing is, this doesn’t just revalidate the data for this Index component, but any route that is currently active — this could be several with nested routing.
So why would we actually want to do this? With the example above, the user could just click the browser’s refresh button 🧠
Well, Supabase Auth takes place client-side, meaning Remix has no idea the state of the user has changed. Without calling the loaders again, we have the stale, unauthenticated version of the data from Supabase. Therefore, when our user signs in, we need to tell Remix to call those loaders again so we can have that fresh, authenticated Supabase data 🥦
Thankfully, Supabase exposes an onAuthStateChange
hook, which takes a callback function to invoke any time the state of the user changes. We can combine these two concepts to tell Remix to revalidate data any time the state of our user changes.
const revalidator = useRevalidator();
supabase.auth.onAuthStateChange(() => {
revalidator.revalidate();
});
And including all the useEffect
and clean-up stuff.
const revalidator = useRevalidator();
useEffect(() => {
const {
data: { subscription },
} = supabase.auth.onAuthStateChange(() => {
revalidator.revalidate();
});
return () => {
subscription.unsubscribe();
};
}, [supabase, revalidator]);
If you want to go much deeper into building a realtime app with Remix and Supabase, check out my brand new, entirely free egghead course. We cover:
- authenticating using GitHub OAuth
- managing auth sessions with cookies to fetch data server-side
- generating TypeScript definitions with the Supabase CLI
- Implementing Row Level Security Policies for authorization
Thanks for reading! 👋