Abhishek
Abhishek7mo ago

Need suggestion on how to store user

I followed the SASS starter approach of storing user in database through useEffect inside a provider component. But I am having this race condition where the provider renders a child which is a server component that require user to be in DB and its throwing error as we are storing user through client component provider. To solve this to make the child a client component but I wanted to ask is there any other way to go around this ? Like calling store user through server component but then how to condition it so that its not called everytime Provider Component
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
<Authenticated>
<StoreUserInDatabase />
{children}
</Authenticated>
<AuthLoading>
<Loading />
</AuthLoading>
</ConvexProviderWithClerk>

function StoreUserInDatabase() {
const { user } = useUser();
const storeUser = useMutation(api.users.store);
useEffect(() => {
void storeUser();
}, [storeUser, user?.id]);
return null;
}
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
<Authenticated>
<StoreUserInDatabase />
{children}
</Authenticated>
<AuthLoading>
<Loading />
</AuthLoading>
</ConvexProviderWithClerk>

function StoreUserInDatabase() {
const { user } = useUser();
const storeUser = useMutation(api.users.store);
useEffect(() => {
void storeUser();
}, [storeUser, user?.id]);
return null;
}
6 Replies
Abhishek
AbhishekOP7mo ago
Here is the child component
async function Home() {
const token = await getAuthToken();
const allCampaignData = await preloadQuery(
api.campaign.getCurrentUserCampaigns,
{},
{ token },
);

return (
<section className="w-full text-stone-800">
<h1 className="pb-1 font-urban text-3xl font-semibold text-black">
Campaigns
</h1>
<p className="pb-8 text-base text-purple-500">
Create, view and manage your LinkedIn campaigns
</p>
<p className="text-slate-600">
Get started by creating a new campaign for LinkedIn
</p>

<Plus widths={18} height={18} strokeWidth={1.5} />
<span>New campaign</span>
</div>
</Link>
{allCampaignData && (
<CampaignListSection allCampaignDataPromise={allCampaignData} />
)}
</section>
);
}

export default Home;
async function Home() {
const token = await getAuthToken();
const allCampaignData = await preloadQuery(
api.campaign.getCurrentUserCampaigns,
{},
{ token },
);

return (
<section className="w-full text-stone-800">
<h1 className="pb-1 font-urban text-3xl font-semibold text-black">
Campaigns
</h1>
<p className="pb-8 text-base text-purple-500">
Create, view and manage your LinkedIn campaigns
</p>
<p className="text-slate-600">
Get started by creating a new campaign for LinkedIn
</p>

<Plus widths={18} height={18} strokeWidth={1.5} />
<span>New campaign</span>
</div>
</Link>
{allCampaignData && (
<CampaignListSection allCampaignDataPromise={allCampaignData} />
)}
</section>
);
}

export default Home;
One way that I am thinking is to call store inside server component but before calling it we can call query to check if user is present in db but again we will be calling unnecessary query and mutation everytime
Michal Srb
Michal Srb7mo ago
I think saas-starter stores the user during the sign-in redirect? That way you can be sure that the user is in the db when you render the signed-in page.
Abhishek
AbhishekOP7mo ago
@Abhishek So here is snippet from the saas starter https://github.com/xixixao/saas-starter/blob/main/app/ConvexClientProvider.tsx#L23
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
<Authenticated>
<StoreUserInDatabase />
</Authenticated>
{children}
</ConvexProviderWithClerk>
</ClerkProvider>
</ErrorBoundary>
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
<Authenticated>
<StoreUserInDatabase />
</Authenticated>
{children}
</ConvexProviderWithClerk>
</ClerkProvider>
</ErrorBoundary>
Here we are calling store in db when user is authenticated but what I am doing is like if the user is authenticated then only render the child
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
<Authenticated>
<StoreUserInDatabase />
{children}
</Authenticated>
<AuthLoading>
<Loading />
</AuthLoading>
</ConvexProviderWithClerk>
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
<Authenticated>
<StoreUserInDatabase />
{children}
</Authenticated>
<AuthLoading>
<Loading />
</AuthLoading>
</ConvexProviderWithClerk>
GitHub
saas-starter/app/ConvexClientProvider.tsx at main · xixixao/saas-st...
Convex, Clerk, Next.js, Convex Ents. Contribute to xixixao/saas-starter development by creating an account on GitHub.
Michal Srb
Michal Srb7mo ago
This is where saas-starter also stores during login: https://github.com/xixixao/saas-starter/blob/main/app/t/page.tsx#L15
GitHub
saas-starter/app/t/page.tsx at main · xixixao/saas-starter
Convex, Clerk, Next.js, Convex Ents. Contribute to xixixao/saas-starter development by creating an account on GitHub.
Michal Srb
Michal Srb7mo ago
But it also always handles the unauthenticated case instead of throwing in functions: https://github.com/xixixao/saas-starter/blob/main/convex/users/teams.ts#L15-L20
GitHub
saas-starter/convex/users/teams.ts at main · xixixao/saas-starter
Convex, Clerk, Next.js, Convex Ents. Contribute to xixixao/saas-starter development by creating an account on GitHub.
Abhishek
AbhishekOP7mo ago
Got thanks

Did you find this page helpful?