React Query hooky
Feature-organizované hooky, Orval generování, STALE_TIMES a architektura
Frontend hooky jsou organizovány podle feature v apps/admin/app/hooks/. Všechny API volání používají Orval-generované hooky z @turtor/api-types.
Adresářová struktura
apps/admin/app/hooks/
├── index.ts # Barrel export (import z ~/hooks)
├── constants.ts # STALE_TIMES, GC_TIMES
├── auth/ # useCurrentUser, useLogout, useSwitchRole, ...
├── instructor/ # usePendingConfirmations, useInstructorCourses, ...
├── course/ # useCourseList, useKanbanCourses, useTeamActions, ...
├── company/ # useCompanyList, useCompanyMembers, ...
├── garant/ # useApplications, useReplacements, useProposals, ...
├── sales/ # useLeads, usePipeline, useCreateLead, ...
├── admin/ # useAdminStats, useSystemHealth
├── notifications/ # useNotificationList, useUnreadNotificationCount, ...
├── profile/ # useFormState
├── withdrawal/ # usePendingWithdrawalRequests, useReviewWithdrawalRequest
├── verify/ # usePhoneVerification
├── realtime/ # useRealtimeUpdates
└── utils/ # useTablePagination, useMutationWithToast, queryKeysImport vzor
Všechny hooky se importují z jednoho barrel exportu:
// Správně -- z barrel exportu
import { useMyProfile, useNotifications, STALE_TIMES } from '~/hooks';
// Špatně -- přímý import
import { useMyProfile } from '~/hooks/instructor/useProfile';Stale Time konstanty
import { STALE_TIMES, GC_TIMES } from '~/hooks';
// STALE_TIMES -- jak dlouho jsou data považována za čerstvá
STALE_TIMES.USER_DATA // 30s -- auth, notifikace
STALE_TIMES.LISTS // 1m -- seznamy kurzů, instruktorů
STALE_TIMES.STATS // 5m -- dashboard statistiky
STALE_TIMES.CONFIG // 30m -- města, kraje, konfigurační data
// GC_TIMES -- jak dlouho zůstávají data v cache po odpojení
GC_TIMES.SHORT // 10m -- často přistupovaná data
GC_TIMES.DEFAULT // 30m -- výchozí
GC_TIMES.LONG // 1h -- referenční dataKlíčové principy
1. Pouze Orval-generované hooky
Všechna API volání musí používat hooky z @turtor/api-types:
// Správně
import { useInstructorsControllerFindAllApiV1 } from '@turtor/api-types/instructors';
// Špatně -- nikdy nepoužívat přímý apiClient v hook souborech
const data = await apiClient.get('/instructors');2. Region-aware dotazy
Admin hooky automaticky injektují regionId z ManageRegionContext:
export function useCourseList({ filters } = {}) {
const regionId = useCurrentRegionId();
const isRegionReady = useIsRegionReady();
return useCoursesControllerFindAllApiV1(
{ ...filters, regionId },
{ query: { enabled: isRegionReady } }
);
}3. WebSocket = invalidace cache
WebSocket eventy spouští invalidaci React Query cache, ne lokální stav:
// Správně -- invalidovat query, React Query refetchne
socket.on('notification', () => {
queryClient.invalidateQueries({ queryKey: queryKeys.notifications.all });
});
// Špatně -- lokální stav vedle React Query
const [notifications, setNotifications] = useState([]);4. Optimistické aktualizace
Optimistické aktualizace pouze pro nedestruktivní operace:
- Status přepínání (schválení/odmítnutí, přečteno/nepřečteno)
- Změny dostupnosti
- Přepínání rolí
Ne pro: smazání, platby, vytváření (čekat na server ID).
5. useMutationWithToast
Factory hook pro standardní mutace s toast notifikacemi:
export function useGenerateCourse() {
return useMutationWithToast({
mutationFn: (data) => generateMutation.mutateAsync({ data }),
successMessage: 'Kurz byl vygenerován',
errorMessage: 'Nepodařilo se vygenerovat kurz',
invalidateKeys: [queryKeys.generation.all],
});
}Utility hooky
| Hook | Soubor | Účel |
|---|---|---|
useCurrentRegionId | useRegionQueryParams.ts | Aktuální region z kontextu |
useIsRegionReady | useRegionQueryParams.ts | Kontrola, zda je region načtený |
useTablePagination | useTablePagination.ts | URL-synchronizovaná paginace pro tabulky |
useMutationWithToast | useMutationWithToast.ts | Factory: mutace + toast + invalidace |
queryKeys | queryKeys.ts | Manuální query key factory |
Stránkovaná odpověď
API vrací stránkované odpovědi ve formátu { data: T[], meta }:
const query = useCoursesControllerFindAllApiV1(params);
// Správně -- extrahovat data z paginované odpovědi
return {
...query,
data: query.data?.data ?? [], // T[] z response.data
meta: query.data?.meta, // Metadata paginace
};Web aplikace -- hooky
Web app má minimální sadu hooků:
apps/web/app/hooks/
├── auth/ # useCurrentUser, useLogin, useRegister, useSendMagicLink, ...
├── checkout/ # useCheckout
├── course/ # useCourseList, useCourseDetail, useBookCourse
└── profile/ # useMyBookings, useUpdateProfile, useChangePasswordNavíc specifické hooky:
useGeolocation-- GPS poloha uživatele, výpočet vzdálenostiuseAresLookup-- vyhledávání firem v ARES (IČO lookup)