)
}
```
### `useScrollPosition`
Preserves scroll position across navigation for better UX.
**Features:**
- Automatic scroll position saving
- Restoration on component mount
- Debounced save operations
- SessionStorage-based persistence
**Usage:**
```typescript
import { useScrollPosition } from '@/hooks'
function MyComponent({ listId }) {
// Automatically saves and restores scroll position
useScrollPosition({
key: `list-${listId}`,
enabled: true,
debounceMs: 100
})
return (
{/* Your scrollable content */}
)
}
```
### `useContainerScrollPosition`
Similar to `useScrollPosition` but for specific container elements.
**Usage:**
```typescript
import { useContainerScrollPosition } from '@/hooks'
function MyComponent({ listId }) {
const { containerRef } = useContainerScrollPosition({
key: `container-${listId}`
})
return (
{/* Your scrollable content */}
)
}
```
## Requirements Validation
These hooks implement the following requirements from the specification:
- **Requirement 6.4**: Filter state management and clearing
- **Requirement 7.3**: Visual feedback for user actions (optimistic updates)
- **Requirement 7.4**: Scroll position preservation
- **Requirement 8.3**: Search clearing and restoration
## Best Practices
1. **Search Debouncing**: Use appropriate debounce times (300ms is a good default)
2. **Filter State**: Keep filter state in sync with URL params for shareable links
3. **Optimistic Updates**: Always provide rollback on error
4. **Scroll Position**: Use unique keys for different views to avoid conflicts
5. **Error Handling**: Always handle errors in async callbacks
## Testing
These hooks are designed to be testable with React Testing Library:
```typescript
import { renderHook, act } from '@testing-library/react'
import { useSearch } from '@/hooks'
test('useSearch debounces queries', async () => {
const onSearch = jest.fn()
const { result } = renderHook(() => useSearch({ onSearch, debounceMs: 100 }))
act(() => {
result.current.setQuery('test')
})
expect(onSearch).not.toHaveBeenCalled()
await act(async () => {
await new Promise(resolve => setTimeout(resolve, 150))
})
expect(onSearch).toHaveBeenCalledWith('test')
})
```