Next.js Discord

Discord Forum

Building out session handler

Answered
GMatrixGames posted this in #help-forum
Open in Discord
Avatar
Heyo, new here!

I've built out a custom session handler (I have my own auth solution) for client routes, and it works great.
My primary issue is becoming that between navigations, the context is reset fully so the user data has to be re-pulled all over again.
I'm probably over thinking it quite a bit, but I'd rather that than not think enough lol.

It'd be nice to also be able to access it through server components/pages as well, but that'll be adjusted later for me.

Any help is appreciated!

"use client"

import * as React from "react";
import {fetchUserInfo, User, validateToken} from "@/lib/auth/api";

interface Session {
    isLoading: boolean;
    isValid: boolean;
    user: User | null;
}

interface SessionContextProps {
    session: Session;
    setSession: React.Dispatch<React.SetStateAction<Session>>;
}

export interface SessionProviderProps {
    children: React.ReactNode;
}

export const SessionContext = React.createContext<SessionContextProps | undefined>(undefined);

export const useSession = (): SessionContextProps => {
    const context = React.useContext(SessionContext);
    if (!context) {
        throw new Error("useSession must be used within a SessionProvider");
    }
    return context;
};

export function SessionProvider(props: SessionProviderProps) {
    const [session, setSession] = React.useState<Session>({isLoading: true, isValid: false, user: null});

    const isInitialLoad = React.useRef(true);
    const isFetchingUser = React.useRef(false);

    React.useEffect(() => {
        const validateAndFetchUser = async () => {
            const {isValid, isNewAccess} = await validateToken();
            setSession(prevSession => ({...prevSession, isValid}));

            if (isValid && (isNewAccess || !session.user) && !isFetchingUser.current) {
                isFetchingUser.current = true;
                const user = await fetchUserInfo();
                setSession(prevSession => ({...prevSession, user, isLoading: false}));
                isFetchingUser.current = false;
            } else if (!isValid) {
                setSession(prevSession => ({...prevSession, user: null, isLoading: false}));
            }

            setSession(prevSession => ({...prevSession, isLoading: false}));
        };

        if (isInitialLoad.current) {
            validateAndFetchUser();
            isInitialLoad.current = false;
        }

        const interval = setInterval(validateAndFetchUser, 30000);
        return () => clearInterval(interval);
    }, [session.user]);

    return (
        <SessionContext.Provider value={{session, setSession}}>
            {props.children}
        </SessionContext.Provider>
    );
}
Answered by GMatrixGames
Turns out I have to use NextJS' Link component rather than the one I have from tdesign-react (component library).
I'll be looking into forwarding the initial state queried by the root layout as well to improve initial loading!
View full answer

1 Reply

Avatar
Turns out I have to use NextJS' Link component rather than the one I have from tdesign-react (component library).
I'll be looking into forwarding the initial state queried by the root layout as well to improve initial loading!
Answer