I am building an app using Ionic React and Supabase. I have some conditional routes where the conditions are based on a context variable (auth.session
). The variable however is null at the time of evaluation, and only gets set by the Supabase client in a useEffect
in my authContextProvider
after the redirect to the sign in page has already happened.
Currently
When there is a session and I navigate to /home
, the router navigates to /sign-in
.
Wanted outcome
When there is a session and I navigate to /home
, the router navigates to /home
.
src/index.tsx
...
root.render(
<React.StrictMode>
<AuthContextProvider>
<App />
</AuthContextProvider>
</React.StrictMode>
);
...
src/App.tsx
...
const App: React.FC = () => {
const { auth } = useContext(AuthContext);
function handleRefresh(event: CustomEvent<RefresherEventDetail>) {
window.location.reload();
}
return (
<IonApp>
<IonContent>
<IonReactRouter>
<IonRouterOutlet id="auth">
<Switch>
<Route exact path="/sign-in" component={SignIn} />
<IonSplitPane contentId="main">
<Menu />
<IonRouterOutlet id="main">
<Route
exact
path="/home"
render={() => {
console.log(auth.session ? "Session" : "No session");
return auth.session ? (
<Home />
) : (
<Redirect to="/sign-in" />
);
}}
/>
<Redirect exact from="/" to="/home" />
</IonRouterOutlet>
</IonSplitPane>
</Switch>
</IonRouterOutlet>
</IonReactRouter>
</IonContent>
</IonApp>
);
};
console
No session
However in state:
src/components/AuthContextProvider.tsx
function AuthContextProvider({ children }: PropsWithChildren) {
const [auth, dispatch] = useReducer(authReducer, {
user: null,
session: null,
});
useEffect(() => {
supabase.auth.getSession().then(({ data }) => {
dispatch({
type: AuthReducerActions.set_auth,
payload: data.session,
});
});
const { data: authListener } = supabase.auth.onAuthStateChange(
(event, session) => {
dispatch({
type: AuthReducerActions.set_auth,
payload: session,
});
}
);
return () => {
authListener.subscription.unsubscribe();
};
}, []);
return (
<AuthContext.Provider value={{ auth, dispatch }}>
{children}
</AuthContext.Provider>
);
}
src/reducers/authReducer.ts
export default function authReducer(
prevState: Auth,
action: AuthReducerAction
): Auth {
switch (action.type) {
case AuthReducerActions.set_auth: {
return {
...prevState,
user: action.payload?.user ?? null,
session: action.payload,
};
}
case AuthReducerActions.sign_out: {
return {
...prevState,
user: null,
session: null,
};
}
default: {
return prevState;
}
}
}
My thought was that when auth.session
is set, that would trigger a rerender by the router. But apparently, it is only executed immediately and then forgets about the context.
This solution using firebase, which is pretty similar to Supabase anyway uses an extra boolean in a hook. Based on that a loading spinner gets rendered or the actual app. If I do this I need to find a way of splitting the auth router and main router.
How would you approach this? Thanks in advance