Skip to content

Edvins Antonovs

Using React Context for Supabase state management

To manage state in React you have many different options, but combining Supabase with React Context is a powerful way to keep your app's state in sync with your backend. In this post, I'll show you the code snipped which I have used in my project to manage the state of the user subscription status, but you can adapt it to manage any state in your app.


Setting up your context

We'll create a ProfileContext that can hold any piece of state you want to manage. In this example, we'll track if a user is subscribed, but you can change it to anything else.

Here's the full code:

profileContext.tsx
1import {
2 createContext,
3 useState,
4 useMemo,
5 ReactNode,
6 useContext,
7 useCallback,
8 useEffect,
9} from 'react';
10import { useSupabaseClient, useUser } from '@supabase/auth-helpers-react';
11import toast from 'react-hot-toast';
12
13interface ProfileContextProps {
14 isSubscribed: boolean;
15}
16
17interface ProfileProviderProps {
18 children: ReactNode;
19}
20
21const ProfileContext = createContext<ProfileContextProps>({
22 isSubscribed: false,
23});
24
25const ProfileProvider = ({ children }: ProfileProviderProps) => {
26 const supabase = useSupabaseClient();
27 const user = useUser();
28
29 const [isSubscribed, setIsSubscribed] = useState<boolean>(false);
30
31 const fetchProfile = useCallback(async () => {
32 try {
33 let { data, error } = await supabase
34 .from('profiles')
35 .select('isSubscribed')
36 .eq('id', user?.id)
37 .single();
38
39 if (error) throw error;
40
41 if (data) {
42 setIsSubscribed(data.isSubscribed);
43 }
44 } catch (error) {
45 toast('An error occurred. Please try again later.');
46 console.log(error);
47 }
48 }, [supabase, user]);
49
50 useEffect(() => {
51 if (user) {
52 fetchProfile();
53 }
54 }, [fetchProfile, user]);
55
56 return (
57 <ProfileContext.Provider
58 value={useMemo(
59 () => ({
60 isSubscribed,
61 }),
62 [isSubscribed]
63 )}
64 >
65 {children}
66 </ProfileContext.Provider>
67 );
68};
69
70const useProfileContext = () => {
71 return useContext(ProfileContext);
72};
73
74export { useProfileContext, ProfileContext, ProfileProvider };

So what happens here? Let's break it down:

  1. We create a ProfileContext with a default value for isSubscribed.
  2. Profile provider component:
  • We use useState to manage isSubscribed.
  • fetchProfile uses Supabase to get the subscription status from the profiles table.
  • useEffect calls fetchProfile whenever the user changes.
  • useMemo ensures the context value updates only when isSubscribed changes.
  • useProfileContext makes it easy to access the context in your components.

Note #1: This code assumes you have a profiles table in your Supabase database with a column isSubscribed. You can adjust it to fit your schema.

Note #2: react-hot-toast is used for displaying error messages. You can replace it with any other toast library or error handling mechanism.


How to use it

Wrap your components with ProfileProvider and use the useProfileContext hook to get the context values.

App.tsx
1import { ProfileProvider, useProfileContext } from './profileContext';
2
3const App = () => {
4 return (
5 <ProfileProvider>
6 <MainComponent />
7 </ProfileProvider>
8 );
9};
10
11const MainComponent = () => {
12 const { isSubscribed } = useProfileContext();
13
14 return (
15 <div>
16 {isSubscribed ? <PremiumContent /> : <StandardContent />}
17 </div>
18 );
19};

Combining React Context with Supabase is a great way to manage state in your app. This example shows how to keep your app's state in sync with your backend easily. Whether it's subscription status or any other piece of state, this approach helps you maintain clean and efficient code.

Got questions or comments? Feel free to leave them below.

© 2024 by Edvins Antonovs. All rights reserved.