import React, { createContext, useContext, useEffect, useMemo, useState, useCallback } from 'react'; import { Customer, fetchMe, logoutCustomer } from './customerApi'; interface CustomerAuthValue { customer: Customer | null; loading: boolean; refresh: () => void; logout: () => Promise; } const CustomerAuthContext = createContext({ customer: null, loading: true, refresh: () => {}, logout: async () => {} }); export function useCustomerAuth() { return useContext(CustomerAuthContext); } export function CustomerAuthProvider({ children }: Readonly<{ children: React.ReactNode }>) { const [customer, setCustomer] = useState(null); const [loading, setLoading] = useState(true); const refresh = useCallback(() => { setLoading(true); // A rejection here previously left `loading` true forever, which renders // as a permanent spinner rather than a signed-out page. void fetchMe() .then(c => setCustomer(c)) .catch(() => setCustomer(null)) .finally(() => setLoading(false)); }, []); // `refresh` sets a pending flag before fetching. The rule cannot tell a // fetch with a spinner from a value that was already knowable, and this is // the former. // eslint-disable-next-line react-hooks/set-state-in-effect useEffect(() => { refresh(); }, [refresh]); // Logging out has to clear the context, not just call the endpoint — // otherwise `customer` stays set and the header keeps offering "My Account" // until something happens to refetch. Clearing here rather than calling // refresh() avoids a window where the session is gone but the UI still shows // the customer as signed in. // // Rejects if the server did not accept the logout, leaving the customer // signed in, because the session cookie is still valid in that case. const logout = useCallback(async () => { await logoutCustomer(); setCustomer(null); setLoading(false); }, []); // Memoized because this is the outermost provider: an unmemoized value makes // every signed-in-aware component in the tree re-render on any parent render. const value = useMemo( () => ({ customer, loading, refresh, logout }), [customer, loading, refresh, logout] ); return ( {children} ); }