home / skills / blockmatic-icebox / basilic-old / ahooks-v3

ahooks-v3 skill

/.cursor/skills/ahooks-v3

This skill helps you manage grouped React state and localStorage persistence with ahooks, improving form, settings, and UI consistency.

npx playbooks add skill blockmatic-icebox/basilic-old --skill ahooks-v3

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.7 KB
---
name: ahooks
description: |
  Utility hooks for React state management - grouped state and localStorage persistence.
  
  Use when: managing grouped state that changes together, persisting state to localStorage, or coordinating state between URL and storage.
---

# Skill: ahooks

## Scope

- Applies to: ahooks v3+ utility hooks for React state management, grouped state updates, localStorage persistence
- Does NOT cover: Data fetching (use TanStack Query), URL state management (use nuqs), complex state machines, async operations

## Assumptions

- ahooks v3+
- React 18+ with hooks support
- TypeScript v5+ (for type inference)
- Client-side only (hooks require browser APIs for localStorage)

## Principles

- Use `useSetState` for grouped state that changes together (not URL-shareable)
- Use `useLocalStorageState` for persistent state across browser sessions
- Prefer nuqs for URL-shareable state over localStorage
- Prefer TanStack Query for async operations and loading states
- Use `useState` only for simple independent state (rare)

## Constraints

### MUST

- Use `useSetState` for grouped state not in URL (form state, game engine state, ephemeral UI state)
- Use `useLocalStorageState` for localStorage persistence

### SHOULD

- Prefer nuqs for URL-shareable state over localStorage
- Prefer TanStack Query for async operations and loading states
- Use TypeScript generics for type-safe state: `useLocalStorageState<boolean>('key', { defaultValue: false })`

### AVOID

- Using for URL state (use nuqs instead)
- Using for loading/error states (use TanStack Query instead)
- Using for complex async operations (use TanStack Query instead)
- Mixing URL state with localStorage without explicit sync logic

## Interactions

- Complements nuqs for URL state management (can sync bidirectionally)
- Complements TanStack Query for async operations (use TanStack Query for data fetching)
- Part of state management decision tree (see React rules)
- Works with React 18+ hooks architecture

## Patterns

### useSetState Pattern

Use for grouped state that updates together:

```typescript
import { useSetState } from 'ahooks'

const [state, setState] = useSetState({
  name: '',
  email: '',
  age: 0,
})

// Partial updates (shallow merge)
setState({ name: 'John' })
setState(prev => ({ ...prev, email: '[email protected]' }))
```

**When to use**: Form state, game engine state, UI state that changes together (if not URL-shareable)

**When NOT to use**: URL-shareable state (use nuqs), loading states (use TanStack Query)

### useLocalStorageState Pattern

Use for state that persists across browser sessions:

```typescript
import { useLocalStorageState } from 'ahooks'

const [value, setValue] = useLocalStorageState<boolean>('key', {
  defaultValue: false,
})

// Type-safe with generics
const [settings, setSettings] = useLocalStorageState<Settings>('settings', {
  defaultValue: { theme: 'light', fontSize: 14 },
})
```

**When to use**: User preferences, debug flags, settings that should persist across sessions

**When NOT to use**: URL-shareable state (use nuqs), sensitive data (use secure storage)

### Integration with nuqs

Bidirectional sync between URL and localStorage:

```typescript
import { useLocalStorageState } from 'ahooks'
import { useQueryState } from 'nuqs'
import { useEffect, useRef } from 'react'

const [queryState, setQueryState] = useQueryState('debug')
const [storageState, setStorageState] = useLocalStorageState<boolean>('debug', {
  defaultValue: false,
})
const isFirstMount = useRef(true)

// Sync on mount: localStorage → URL
useEffect(() => {
  if (isFirstMount.current) {
    isFirstMount.current = false
    if (storageState && queryState !== 'true') {
      setQueryState('true')
      return
    }
  }
  // Sync changes: URL → localStorage
  if (queryState === 'true' && !storageState) {
    setStorageState(true)
  } else if (queryState === 'false' && storageState) {
    setStorageState(false)
  }
}, [queryState, storageState, setQueryState, setStorageState])
```

**Use case**: Debug flags, feature toggles that should be both URL-shareable and persistent

### State Management Decision Tree

1. **URL-shareable state** → Use `nuqs` (filters, search, tabs, pagination)
2. **Grouped state not in URL** → Use `useSetState` (form state, game engine, ephemeral UI)
3. **Async operations** → Use TanStack Query (data fetching, mutations, caching)
4. **localStorage persistence** → Use `useLocalStorageState` (preferences, settings)
5. **Simple independent state** → Use `useState` (rare, prefer other options)

## References

- [ahooks documentation](https://ahooks.js.org/) - Official documentation
- React rules - State management decision tree and patterns
- TanStack Query - Async operations and data fetching patterns

Overview

This skill provides concise guidance for using ahooks v3+ utility hooks for React state management, focusing on grouped state updates and localStorage persistence. It explains when to pick useSetState vs useLocalStorageState and how they fit with URL state and async patterns. The goal is practical recommendations for React 18+ projects using TypeScript.

How this skill works

It inspects common state scenarios and recommends the appropriate hook: useSetState for shallow-merged grouped state updates, and useLocalStorageState for values that must persist across browser sessions. It also describes integration patterns (bidirectional sync) with URL state managers like nuqs and clarifies what to avoid (loading/error states and complex async flows).

When to use it

  • Grouped state that changes together (forms, UI panels, game state) → useSetState
  • State that must persist across sessions (preferences, toggles) → useLocalStorageState
  • When you need type safety with TypeScript generics for stored values
  • When syncing shareable flags between URL and storage (use bidirectional sync with nuqs)
  • Avoid for async loading/error state or complex state machines — use TanStack Query instead

Best practices

  • Use useSetState for partial, shallow-merged updates instead of many individual useState hooks
  • Use useLocalStorageState<T>('key', { defaultValue }) with TypeScript generics for type-safe persistence
  • Prefer nuqs for URL-shareable state and keep localStorage for non-URL persistence or explicit sync scenarios
  • Don’t store sensitive data in localStorage; avoid mixing URL and storage without explicit sync logic
  • Use TanStack Query for data fetching, caching, and loading/error state rather than ahooks hooks

Example use cases

  • Form editor where multiple fields update together → useSetState for concise partial updates
  • User theme, font size, or debug flags persisted across reloads → useLocalStorageState
  • Feature toggle that should be both URL-shareable and persistent → sync nuqs query state with useLocalStorageState
  • Ephemeral UI state like panel visibility or in-memory game level state → useSetState
  • Preferences with strict TypeScript types (Settings object) → useLocalStorageState<Settings>('settings', { defaultValue })

FAQ

Can I use useLocalStorageState for URL-shareable settings?

Prefer nuqs for URL-shareable state. If you must persist to localStorage too, implement an explicit bidirectional sync to avoid inconsistent sources of truth.

Is useSetState suitable for async loading or error states?

No. Use TanStack Query for async operations and loading/error handling; useSetState is best for grouped synchronous UI state.