So I have this kind of issue, having a React Context where I manage authentication I use a protected routes component to disable accessing pages when the user is not authenticated (no localstorage user data available).
But there is a problem with this approach, because at the first pre-render the user is undefined (useState()
) and only after useEffect runs the user data is fetched.
Snippet code:
AuthContextProvider
const [user, setUser] = useState();
const [hydrated, setHydrated] = useState(false); // added to fix the problem
useEffect(() => {
const userLocal = localStorage.getItem('user');
setUser(JSON.parse(userLocal));
setHydrated(true); // added to fix the problem
}, []);
Protected routes:
if (isBrowser() && !authCtx.user && pathIsProtected) {
router.push('/signin');
}
So I end up having user = undefined for a fraction of second that is enough to catch the condition !authCtx.user
, instead the user is authenticated.
As you can see in the code my workaround that works is to use a hydrated
variable which I set together with the user data from localstorage and in the provider I wait the hydrated variable to be true before rendering the children components (where the protected route is)
return (
<AuthContext.Provider
value={{
error: error,
loading: loading,
user: user
}}
>
{hydrated && children} {/* added to fix the problem */}
</AuthContext.Provider>
);
This way everything works fine but is this the right pattern for this kind of problem?
You can do this, which is almost the same but uses user
variable instead of hydrated
:
const [user, setUser] = useState(false);
And in the component:
...
>
{user && children} {/* added to fix the problem */}
</AuthContext.Provider>
...
This way you can drop const [hydrated, setHydrated] = useState(false);
altogether.