feat(globalStateStore): implement global state management with persistence
- refactor state structure to include app, layout, navigation, owner, program, session, and user - add slices for managing program, session, owner, user, layout, navigation, and app states - create context provider for global state with automatic fetching and throttling - implement persistence using IndexedDB with localStorage fallback - add comprehensive README documentation for usage and API
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import { createContext, type ReactNode, useCallback, useContext, useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
import type { GlobalStateStoreType } from './GlobalStateStore.types';
|
||||
|
||||
import { GetGlobalState, GlobalStateStore } from './GlobalStateStore';
|
||||
|
||||
|
||||
interface GlobalStateStoreContextValue {
|
||||
fetchData: (url?: string) => Promise<void>;
|
||||
getState: () => GlobalStateStoreType;
|
||||
refetch: () => Promise<void>;
|
||||
}
|
||||
|
||||
const GlobalStateStoreContext = createContext<GlobalStateStoreContextValue | null>(null);
|
||||
|
||||
|
||||
interface GlobalStateStoreProviderProps {
|
||||
apiURL?: string;
|
||||
autoFetch?: boolean;
|
||||
children: ReactNode;
|
||||
fetchOnMount?: boolean;
|
||||
throttleMs?: number;
|
||||
}
|
||||
|
||||
export function GlobalStateStoreProvider({
|
||||
apiURL,
|
||||
autoFetch = true,
|
||||
children,
|
||||
fetchOnMount = true,
|
||||
throttleMs = 0,
|
||||
}: GlobalStateStoreProviderProps) {
|
||||
const lastFetchTime = useRef<number>(0);
|
||||
const fetchInProgress = useRef<boolean>(false);
|
||||
const mounted = useRef<boolean>(false);
|
||||
|
||||
|
||||
const throttledFetch = useCallback(
|
||||
async (url?: string) => {
|
||||
const now = Date.now();
|
||||
const timeSinceLastFetch = now - lastFetchTime.current;
|
||||
|
||||
if (fetchInProgress.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (throttleMs > 0 && timeSinceLastFetch < throttleMs) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
fetchInProgress.current = true;
|
||||
lastFetchTime.current = now;
|
||||
await GlobalStateStore.getState().fetchData(url);
|
||||
} finally {
|
||||
fetchInProgress.current = false;
|
||||
}
|
||||
},
|
||||
[throttleMs]
|
||||
);
|
||||
|
||||
const refetch = useCallback(async () => {
|
||||
await throttledFetch();
|
||||
}, [throttledFetch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (apiURL) {
|
||||
GlobalStateStore.getState().setApiURL(apiURL);
|
||||
}
|
||||
}, [apiURL]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mounted.current) {
|
||||
mounted.current = true;
|
||||
|
||||
if (autoFetch && fetchOnMount) {
|
||||
throttledFetch(apiURL).catch((e) => {
|
||||
console.error('Failed to fetch on mount:', e);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [apiURL, autoFetch, fetchOnMount, throttledFetch]);
|
||||
|
||||
const context = useMemo(() => {
|
||||
return {
|
||||
fetchData: throttledFetch,
|
||||
getState: GetGlobalState,
|
||||
refetch,
|
||||
};
|
||||
}, [throttledFetch, refetch]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<GlobalStateStoreContext.Provider value={context}>
|
||||
{children}
|
||||
</GlobalStateStoreContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export function useGlobalStateStoreContext(): GlobalStateStoreContextValue {
|
||||
const context = useContext(GlobalStateStoreContext);
|
||||
if (!context) {
|
||||
throw new Error('useGlobalStateStoreContext must be used within GlobalStateStoreProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user