| @@ -1,42 +1,67 @@ | |||
| // src/app/(main)/axios/AxiosProvider.tsx | |||
| "use client"; | |||
| import React, { createContext, useContext, useEffect, useState } from "react"; | |||
| import React, { createContext, useContext, useEffect, useState, useCallback } from "react"; | |||
| import axiosInstance, { SetupAxiosInterceptors } from "./axiosInstance"; | |||
| const AxiosContext = createContext(axiosInstance); | |||
| const TokenContext = createContext({ | |||
| setAccessToken: (token: string | null) => {}, | |||
| const TokenContext = createContext<{ | |||
| setAccessToken: (token: string | null) => void; | |||
| }>({ | |||
| setAccessToken: () => {}, | |||
| }); | |||
| export const AxiosProvider: React.FC<{ children: React.ReactNode }> = ({ | |||
| children, | |||
| }) => { | |||
| const [accessToken, setAccessToken] = useState<string | null>( | |||
| localStorage.getItem("accessToken"), | |||
| ); | |||
| export const AxiosProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { | |||
| const [accessToken, setAccessToken] = useState<string | null>(null); | |||
| const [isHydrated, setIsHydrated] = useState(false); | |||
| // Hydrate token only on client | |||
| useEffect(() => { | |||
| try { | |||
| const token = localStorage.getItem("accessToken"); | |||
| if (token) setAccessToken(token); | |||
| } catch (e) { | |||
| console.warn("localStorage unavailable", e); | |||
| } finally { | |||
| setIsHydrated(true); | |||
| } | |||
| }, []); | |||
| // Apply token + interceptors | |||
| useEffect(() => { | |||
| if (accessToken) { | |||
| axiosInstance.defaults.headers.Authorization = `Bearer ${accessToken}`; | |||
| SetupAxiosInterceptors(accessToken); | |||
| console.log("[debug] Updated accessToken:", accessToken); | |||
| } else { | |||
| delete axiosInstance.defaults.headers.Authorization; | |||
| } | |||
| }, [accessToken]); | |||
| const handleSetAccessToken = useCallback((token: string | null) => { | |||
| setAccessToken(token); | |||
| try { | |||
| if (token) { | |||
| localStorage.setItem("accessToken", token); | |||
| } else { | |||
| localStorage.removeItem("accessToken"); | |||
| } | |||
| } catch (e) { | |||
| // ignore (e.g. private mode) | |||
| } | |||
| }, []); | |||
| // Critical fix: never return null → always return children wrapped in fragment | |||
| return ( | |||
| <AxiosContext.Provider value={axiosInstance}> | |||
| <TokenContext.Provider value={{ setAccessToken }}> | |||
| <TokenContext.Provider value={{ setAccessToken: handleSetAccessToken }}> | |||
| {/* Render children immediately – they will just not have the token for 1-2ms */} | |||
| {children} | |||
| </TokenContext.Provider> | |||
| </AxiosContext.Provider> | |||
| ); | |||
| }; | |||
| // Custom hook to use Axios instance | |||
| export const useAxios = () => { | |||
| return useContext(AxiosContext); | |||
| }; | |||
| // Custom hook to manage access token | |||
| export const useToken = () => { | |||
| return useContext(TokenContext); | |||
| }; | |||
| export const useAxios = () => useContext(AxiosContext); | |||
| export const useToken = () => useContext(TokenContext); | |||