Skip to main content

State Management

Tracera uses a layered approach to state management, choosing the right tool for each type of state.

Architecture

State TypeToolScope
Auth stateReact ContextGlobal — available to all components
UI stateZustandClient-side — persists across navigations
Server stateServer ComponentsPage-level — fetched at render time
Form stateReact hooksComponent-level — local to forms

Auth Context

Authentication state is provided via React Context, initialized server-side:
// lib/auth-context.tsx
const AuthContext = createContext<AuthContextType | null>(null);

export function AuthProvider({ initialUser, children }) {
  const [user, setUser] = useState(initialUser);

  return (
    <AuthContext.Provider value={{ user, setUser }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth must be within AuthProvider');
  return context;
}
The initialUser is fetched server-side in the root layout via getSession(), avoiding a loading state on initial page load.

Zustand Store

Client-side application state uses Zustand for its simplicity and performance:
// lib/store.ts
import { create } from 'zustand';

interface AppStore {
  // State definitions
}

export const useAppStore = create<AppStore>((set) => ({
  // State and actions
}));
Zustand advantages for Tracera:
  • No provider wrapper needed (unlike Redux)
  • Minimal boilerplate
  • Built-in support for middleware (persist, devtools)
  • React 19 compatible

Server-Side Data Fetching

Server Components fetch data directly without client-side state:
// lib/auth.ts
export async function getSession(): Promise<User | null> {
  const res = await serverFetch('/api/v1/auth/me');
  if (!res.ok) return null;
  return res.json();
}
The serverFetch() helper handles:
  • Forwarding cookies for authentication
  • Configuring the correct backend URL (different in Docker vs local)
  • Error handling

Validation

Zod schemas provide runtime type validation:
// lib/schemas.ts
import { z } from 'zod';

export const emailSchema = z.string().email();
export const loginSchema = z.object({
  email: emailSchema,
});
Used for form validation and API response parsing.