---
description: "Hook patterns for dashboard modules: naming conventions, query hooks (GET wrappers), mutation hooks, and custom logic hooks."
paths:
  - "src/pages/dashboard/**"
---

# Module Architecture — Hooks

Hooks exclusive to the module. Each GET service has its 1:1 wrapper hook, same for mutations.
**No `hooks/index.ts` barrel** — import each hook directly.

For the general module structure, see `module-architecture.md`.
For invariants (rules, principles), see `module-architecture-conventions.md`.

---

## Naming convention

- **Queries (GET):** `useGet{Entity}.ts` — wraps `useQuery`, calls the service and returns the data.
  `getSessions` → `useGetSessions`, `getSession` → `useGetSession`.
- **Mutations (POST/PUT/PATCH/DELETE):** `use{Action}{Entity}Mutation.ts` — wraps `useMutation`.
  `confirmSessionAttendance` → `useConfirmAttendanceMutation`, `createGoal` → `useCreateGoalMutation`.
- **Custom logic:** `use{CustomLogic}.ts` — business logic hooks, filters, local state, etc.

> Examples use the object syntax of **react-query v4**. Both repos are currently on v3.39,
> but v3.39 supports this form and it is the API that will be adopted in v4.

---

## Query hook (GET wrapper)

```typescript
// hooks/useGetSessions.ts
import { useQuery, UseQueryOptions } from 'react-query';
import { sessionsKeys } from '../queries';
import { getSessions, GetSessionsResponse } from '../services';

export const useGetSessions = (
  params: GetSessionsParams,
  options?: Omit<
    UseQueryOptions<GetSessionsResponse, Error>,
    'queryKey' | 'queryFn'
  >,
) => {
  const { data, ...query } = useQuery({
    queryKey: sessionsKeys.list(params),
    queryFn: () => getSessions(params),
    select: response => response.data,
    keepPreviousData: true,
    ...options,
  });

  return {
    sessions: data?.items ?? [],
    ...query,
  };
};
```

---

## Mutation hook wrapper

```typescript
// hooks/useConfirmAttendanceMutation.ts
import { useMutation } from 'react-query';
import { queryClient } from 'src/config/react-query';
import { sessionsKeys } from '../queries';
import { confirmSessionAttendance } from '../services';

export const useConfirmAttendanceMutation = (sessionId: number) => {
  return useMutation({
    mutationFn: confirmSessionAttendance,
    onSettled: () => {
      queryClient.invalidateQueries(sessionsKeys.detail(sessionId));
      queryClient.invalidateQueries(sessionsKeys.lists());
    },
  });
};
```

---

## Key rule

Each hook is responsible ONLY for wrapping the service and managing the cache.
UI logic (toast, navigation, etc.) goes in the component that consumes the hook.
