Skip to main content

Client & User Hooks Reference

Client-facing hooks provide authenticated users with tools to manage their own content, interact with items, and handle user preferences. These hooks power the public-facing UI and user dashboards.

useClientItems

Manages the authenticated user's submitted items with status filtering, pagination, and soft-delete/restore support.

import { useClientItems } from '@/hooks/use-client-items';

const {
items, // ClientSubmissionData[]
stats, // { total, draft, pending, approved, rejected, deleted }
total, page, limit, totalPages,
isLoading, isFetching, isStatsLoading,
isUpdating, isDeleting, isRestoring, isSubmitting,
error,
updateItem, // (id: string, data: ClientUpdateItemRequest) => Promise<boolean>
deleteItem, // (id: string) => Promise<boolean> -- soft delete
restoreItem, // (id: string) => Promise<boolean>
refetch, refreshData, prefetchNextPage,
} = useClientItems({ page: 1, limit: 10, status: 'approved', search: '' });

Key features:

  • Separate stats query for independent refresh
  • prefetchNextPage(nextPage) for smooth pagination
  • Soft delete with restore capability
  • Status change detection on update (re-triggers review)

Cache key: ['client', 'items', ...]

useFavorites

User favorites with optimistic updates for instant UI feedback.

import { useFavorites } from '@/hooks/use-favorites';

const {
favorites, // Favorite[]
isLoading,
error,
refetch,
isFavorited, // (itemSlug: string) => boolean
toggleFavorite, // (data: AddFavoriteRequest) => void
addFavorite, // mutation function
removeFavorite, // mutation function
isAdding, // boolean
isRemoving, // boolean
} = useFavorites();

Key features:

  • Optimistic updates on both add and remove -- UI updates immediately, rolls back on error
  • Respects useFeatureFlagsWithSimulation -- only fetches when favorites feature is enabled
  • Requires authenticated user (useCurrentUser)
  • Deduplication logic prevents stale cache entries

Cache key: ['favorites']

useComments

Full comment CRUD with rating support and real-time cache updates.

import { useComments } from '@/hooks/use-comments';

const {
comments, // CommentWithUser[]
isPending,
createComment, // ({ content, itemId, rating }) => Promise<CommentWithUser>
isCreating,
updateComment, // ({ commentId, content?, rating? }) => Promise<CommentWithUser>
isUpdating,
deleteComment, // (commentId: string) => Promise<void>
isDeleting,
rateComment, // ({ commentId, rating }) => void
isRatingComment,
updateCommentRating, // ({ commentId, rating }) => void
isUpdatingRating,
commentRating, // number
isLoadingRating,
} = useComments(itemId);

Key features:

  • Optimistic cache update on create/edit (no full refetch needed)
  • Cross-query rating invalidation -- ["item-rating", itemId] refetched on comment changes
  • Custom event dispatch (comment:mutated) for cross-component coordination
  • Login modal trigger on 401 responses

Cache key: ['comments', itemId]

useItemVote

Upvote/downvote system with optimistic updates and vote toggling.

import { useItemVote } from '@/hooks/use-item-vote';

const {
voteCount, // number -- net vote count
userVote, // 'up' | 'down' | null
isLoading, // boolean
handleVote, // (type: 'up' | 'down') => void
refreshVotes, // () => void
} = useItemVote(itemId);

Behavior:

  • Clicking the same vote type removes the vote (toggle)
  • Clicking the opposite vote type switches the vote
  • Unauthenticated users see a login modal
  • Optimistic count calculation accounts for vote switching

Additional export: useVoteCache() for cross-component cache management (invalidate, clear, prefetch).

Cache key: ['item-votes', itemId]

useFilters

Client-side filtering with URL synchronization.

import { useFilters } from '@/hooks/use-filters';

const {
activeFilters,
setFilter, // (key: string, value: string | string[]) => void
clearFilter, // (key: string) => void
clearAll, // () => void
hasActiveFilters, // boolean
} = useFilters();

useDebouncedSearch

Search input with configurable debounce delay.

import { useDebouncedSearch } from '@/hooks/use-debounced-search';

const {
searchTerm, // string -- current input value
debouncedTerm, // string -- debounced value for API calls
setSearchTerm, // (value: string) => void
clearSearch, // () => void
} = useDebouncedSearch({ delay: 300 });

usePaginatedQuery

Generic paginated query wrapper used by many list hooks.

import { usePaginatedQuery } from '@/hooks/use-paginated-query';

const {
data,
page, totalPages, hasNext, hasPrev,
nextPage, prevPage, goToPage,
isLoading, isFetching,
} = usePaginatedQuery({
queryKey: ['items'],
queryFn: (page) => fetchItems({ page }),
initialPage: 1,
});

useCurrentUser

Provides the authenticated user's session data.

import { useCurrentUser } from '@/hooks/use-current-user';

const { user, isLoading, isAuthenticated } = useCurrentUser();
// user: { id, name, email, image, role }

useInfiniteLoading

Infinite scroll support with intersection observer.

import { useInfiniteLoading } from '@/hooks/use-infinite-loading';

const {
items,
isLoading,
isFetchingNextPage,
hasNextPage,
loadMoreRef, // Ref<HTMLDivElement> -- attach to sentinel element
fetchNextPage,
} = useInfiniteLoading({
queryKey: ['items-infinite'],
queryFn: ({ pageParam }) => fetchItems({ page: pageParam }),
});

UI Utility Hooks

HookPurposeReturns
useMobileResponsive breakpoint detection{ isMobile: boolean }
useDebouncedValueGeneric debounce for any valuedebouncedValue
useStickyStatePersisted state via localStorage[value, setValue]
useThrottledScrollScroll position with throttling{ scrollY, isScrolled }
useScrollToTopProgrammatic scroll-to-top{ scrollToTop: () => void }
useOnClickOutsideClick-outside detectionAttaches handler to ref
useComposedRefMerge multiple refscomposedRef
useLocalStorageType-safe localStorage[value, setValue, remove]
useToastToast notification wrapper{ toast, dismiss }
useLoginModalLogin modal state{ isOpen, onOpen, onClose }
useThemeTheme switching{ theme, setTheme, resolvedTheme }