feat: implement client-side state management hooks
- Created useSearch hook for search state and debouncing - Created useFilters hook for filter state management - Created useOptimisticUpdate hook for optimistic UI updates - Created useScrollPosition hook for scroll preservation - Updated SearchBar, GlobalSearch, and ListDetailView to use new hooks - Added comprehensive documentation for all hooks Validates: Requirements 6.4, 7.3, 7.4, 8.3 Task: 9. Implement client-side state management
This commit is contained in:
84
hooks/useSearch.ts
Normal file
84
hooks/useSearch.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||
|
||||
interface UseSearchOptions {
|
||||
debounceMs?: number
|
||||
onSearch?: (query: string) => void | Promise<void>
|
||||
}
|
||||
|
||||
interface UseSearchReturn {
|
||||
query: string
|
||||
setQuery: (query: string) => void
|
||||
debouncedQuery: string
|
||||
isDebouncing: boolean
|
||||
clearSearch: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom hook for managing search state with debouncing
|
||||
* Validates: Requirements 6.4, 8.3
|
||||
*/
|
||||
export function useSearch(options: UseSearchOptions = {}): UseSearchReturn {
|
||||
const { debounceMs = 300, onSearch } = options
|
||||
|
||||
const [query, setQuery] = useState('')
|
||||
const [debouncedQuery, setDebouncedQuery] = useState('')
|
||||
const [isDebouncing, setIsDebouncing] = useState(false)
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
// Debounce the query
|
||||
useEffect(() => {
|
||||
// If query is empty, update immediately
|
||||
if (!query.trim()) {
|
||||
setDebouncedQuery('')
|
||||
setIsDebouncing(false)
|
||||
return
|
||||
}
|
||||
|
||||
setIsDebouncing(true)
|
||||
|
||||
// Clear existing timeout
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
}
|
||||
|
||||
// Set new timeout
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
setDebouncedQuery(query)
|
||||
setIsDebouncing(false)
|
||||
}, debounceMs)
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
}
|
||||
}
|
||||
}, [query, debounceMs])
|
||||
|
||||
// Call onSearch callback when debounced query changes
|
||||
useEffect(() => {
|
||||
if (onSearch) {
|
||||
onSearch(debouncedQuery)
|
||||
}
|
||||
}, [debouncedQuery, onSearch])
|
||||
|
||||
// Clear search function
|
||||
const clearSearch = useCallback(() => {
|
||||
setQuery('')
|
||||
setDebouncedQuery('')
|
||||
setIsDebouncing(false)
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return {
|
||||
query,
|
||||
setQuery,
|
||||
debouncedQuery,
|
||||
isDebouncing,
|
||||
clearSearch
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user