React Query Pattern

All server state is managed through React Query. Query and mutation hooks are defined centrally, never inline in components.

Organization

All query/mutation hooks live in frontend/src/lib/queries/ (34 modules). Query key hierarchy is centralized in frontend/src/lib/queryKeys.ts.

ModuleEntity
buildingQueries.tsBuildings
projectQueries.tsProjects
contactQueries.tsContacts
quoteQueries.tsQuotes
invoiceQueries.tsInvoices
hubspot-queries.tsHubSpot Integration
workflowQueries.tsWorkflows
documentObtainingQueries.tsDocument Obtaining
scenarioQueries.tsScenarios
fundingApplicationQueries.tsFunding Applications

Key Factory Pattern

Every module exports a key factory using as const for type-safe cache keys:

export const buildingKeys = {
  all: ['buildings'] as const,
  lists: () => [...buildingKeys.all, 'list'] as const,
  detail: (id: string) => [...buildingKeys.all, id] as const,
};

Mutation Hooks

Mutation hooks include cache invalidation, optimistic updates, and toast notifications:

export function useCreateBuilding() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (data: CreateBuildingInput) => api.buildings.create(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: buildingKeys.all });
      toast.success(t('buildings.created'));
    },
  });
}

ESLint Enforcement

An ESLint rule warns when useMutation appears in components/ or pages/ directories. All mutations must be defined in lib/queries/.

Usage in Components

Following Component Decomposition, queries are called in custom hooks (useFooState.ts), not in presentational components:

// useBuildingListState.ts
export function useBuildingListState() {
  const buildings = useBuildings(projectId);
  const createBuilding = useCreateBuilding();
  return { buildings, createBuilding };
}

See Also