Custom Hooks
Overview
Collection of custom React hooks untuk mempermudah state management, data handling, dan logic reuse di aplikasi frontend. Hooks ini dirancang untuk reusable, type-safe, dan framework-agnostic.
Komponen Hooks
useFilterSortHandler
Universal hook untuk managing filters, sorting, dan pagination dalam table/list views.
Features:
- Multiple filter operators (eq, like, in, between, gt, gte, lt, lte)
- Sort status management (ascending/descending)
- Pagination support (page & perPage)
- Query string generation untuk API calls
- Active filter/sort detection
- TypeScript generics support
- URL encoding
- Date formatting
Use Cases:
- Data tables dengan server-side filtering
- Search functionality dengan multiple criteria
- Admin dashboards dengan complex filters
- Paginated list views
import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler'
function UserTable() {
const {
filters,
setFilter,
removeFilter,
sortStatus,
setSortStatus,
page,
setPage,
perPage,
setPerPage,
queryString
} = useFilterSortHandler({
initialPage: 1,
initialPerPage: 10
})
// Set filter
setFilter('name', 'like', 'John')
// Generate query string untuk API
const url = `/api/users?${queryString}` // ?name[like]=John&page=1&perPage=10
}
Filter Handler
Helper functions untuk filter dan sort operations.
Features:
- Filter transformation
- Sort transformation
- Query building
- Type conversion
- Date handling
Use Cases:
- Custom filter logic
- Transform filter data
- Build complex queries
import { buildFilterQuery, transformSortStatus } from '@/utils/hooks/filter-sort-handler'
const filterQuery = buildFilterQuery(filters)
const sortQuery = transformSortStatus(sortStatus)
Sort Handler
Dedicated hook untuk sort operations.
Features:
- Sort column management
- Sort direction toggle
- Multi-column sorting
- Sort state persistence
Use Cases:
- Table column sorting
- List sorting
- Custom sort implementations
import { useSortHandler } from '@/utils/hooks/sort-handler'
const { sortBy, sortDirection, handleSort } = useSortHandler('name', 'asc')
// Toggle sort
handleSort('email') // Sort by email
Common Patterns
Table dengan Filter dan Sort
import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler'
import { DataTable } from 'mantine-datatable'
function DataTableWithFilters() {
const {
filters,
setFilter,
removeFilter,
sortStatus,
setSortStatus,
page,
setPage,
perPage,
setPerPage,
queryString,
hasActiveFilters
} = useFilterSortHandler({
initialPage: 1,
initialPerPage: 10,
initialSort: { columnAccessor: 'name', direction: 'asc' }
})
// Fetch data dengan query string
const { data, isLoading } = useQuery({
queryKey: ['users', queryString],
queryFn: () => fetchUsers(queryString)
})
return (
<div>
{/* Filter Section */}
<div className="filters">
<input
placeholder="Search by name"
onChange={(e) => setFilter('name', 'like', e.target.value)}
/>
<select onChange={(e) => setFilter('status', 'eq', e.target.value)}>
<option value="">All Status</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
{hasActiveFilters && (
<button onClick={removeFilter}>Clear Filters</button>
)}
</div>
{/* Table */}
<DataTable
records={data?.records}
columns={[
{ accessor: 'name', sortable: true },
{ accessor: 'email', sortable: true },
{ accessor: 'status' }
]}
sortStatus={sortStatus}
onSortStatusChange={setSortStatus}
page={page}
onPageChange={setPage}
recordsPerPage={perPage}
onRecordsPerPageChange={setPerPage}
totalRecords={data?.total}
fetching={isLoading}
/>
</div>
)
}
Advanced Filtering
import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler'
function AdvancedSearch() {
const { setFilter, getFilterValue, queryString } = useFilterSortHandler()
// Range filter (between)
const handleDateRange = (start: Date, end: Date) => {
setFilter('created_at', 'between', [start, end])
}
// Multiple values (in)
const handleMultiSelect = (values: string[]) => {
setFilter('category', 'in', values)
}
// Greater than
const handleMinPrice = (price: number) => {
setFilter('price', 'gte', price)
}
// Get current filter value
const currentName = getFilterValue('name') // Returns current filter value
return (
<div>
<input
value={currentName || ''}
onChange={(e) => setFilter('name', 'like', e.target.value)}
/>
{/* Other filters */}
</div>
)
}
Persist Filters in URL
import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler'
import { useSearchParams } from 'react-router-dom'
function PersistentFilters() {
const [searchParams, setSearchParams] = useSearchParams()
const { queryString, filters, sortStatus, page } = useFilterSortHandler({
initialPage: Number(searchParams.get('page')) || 1,
initialPerPage: Number(searchParams.get('perPage')) || 10
})
// Update URL when filters change
useEffect(() => {
setSearchParams(queryString)
}, [queryString])
// Users can share or bookmark filtered URLs
return <div>...</div>
}
Type Safety
Hooks fully support TypeScript dengan generics:
interface User {
id: number
name: string
email: string
status: 'active' | 'inactive'
created_at: Date
}
const {
filters,
setFilter,
sortStatus
} = useFilterSortHandler<User>()
// Type-safe filter keys
setFilter('name', 'like', 'John') // ✅ OK
setFilter('invalid', 'like', 'test') // ❌ TypeScript error
Best Practices
1. Initial State
Set reasonable initial values:
const hook = useFilterSortHandler({
initialPage: 1,
initialPerPage: 25, // Reasonable default
initialSort: { columnAccessor: 'created_at', direction: 'desc' }
})
2. Debounce Text Input
Untuk search/filter text, gunakan debounce:
import { useDebouncedValue } from '@mantine/hooks'
function SearchFilter() {
const [search, setSearch] = useState('')
const [debouncedSearch] = useDebouncedValue(search, 300)
const { setFilter } = useFilterSortHandler()
useEffect(() => {
setFilter('name', 'like', debouncedSearch)
}, [debouncedSearch])
return <input value={search} onChange={(e) => setSearch(e.target.value)} />
}
3. Clear Filters
Provide cara untuk clear filters:
const { removeFilter, hasActiveFilters } = useFilterSortHandler()
{hasActiveFilters && (
<button onClick={removeFilter}>
Clear All Filters
</button>
)}
4. Loading States
Show loading saat fetching data:
const { queryString } = useFilterSortHandler()
const { data, isLoading } = useQuery(['data', queryString], fetchData)
return (
<div>
{isLoading && <Spinner />}
<DataTable records={data} />
</div>
)
5. Error Handling
Handle errors gracefully:
const { data, error, isError } = useQuery(['data', queryString], fetchData)
if (isError) {
return <ErrorMessage error={error} />
}
Performance Tips
1. Memoization
Hook sudah menggunakan useMemo dan useCallback internally untuk optimasi.
2. Lazy Loading
// Only load data when filters change
const { queryString } = useFilterSortHandler()
const { data } = useQuery({
queryKey: ['data', queryString],
queryFn: () => fetchData(queryString),
enabled: !!queryString // Only fetch when queryString exists
})
3. Pagination
Implement proper pagination untuk large datasets:
const { page, perPage, setPage, setPerPage } = useFilterSortHandler({
initialPerPage: 50 // Larger page size for better performance
})
Integration Examples
With React Query
import { useQuery } from '@tanstack/react-query'
import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler'
const { queryString } = useFilterSortHandler()
const { data } = useQuery({
queryKey: ['users', queryString],
queryFn: () => fetch(`/api/users?${queryString}`).then(r => r.json())
})
With Mantine DataTable
import { DataTable } from 'mantine-datatable'
import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler'
const { sortStatus, setSortStatus, page, setPage } = useFilterSortHandler()
<DataTable
sortStatus={sortStatus}
onSortStatusChange={setSortStatus}
page={page}
onPageChange={setPage}
/>
With Form Libraries
import { useForm } from 'react-hook-form'
import { useFilterSortHandler } from '@/utils/hooks/useFilterSortHandler'
const form = useForm()
const { setFilter } = useFilterSortHandler()
form.watch((values) => {
Object.entries(values).forEach(([key, value]) => {
if (value) setFilter(key, 'eq', value)
})
})
Related Components
- UI Components - Components yang menggunakan hooks ini
- Code Generation - Generated hooks dari API
- Utils - Utility functions
Technology Stack: React Hooks, TypeScript
Use Case: Data filtering, Sorting, Pagination, State management
Last Updated: November 2025