---
description: Convert a React hook from thrillwiki-87 to a Vue composable
---

# Migrate Hook Workflow

Convert a React hook from thrillwiki-87 to a Vue 3 composable for the Nuxt 4 project.

## Step 1: Locate Source Hook

Find the React hook in thrillwiki-87:
```bash
/Volumes/macminissd/Projects/thrillwiki-87/src/hooks/
```

Key hooks (80+ total):
- `useAuth.tsx` - Authentication state
- `useModerationQueue.ts` - Moderation logic (21KB)
- `useEntityVersions.ts` - Version history (14KB)
- `useSearch.tsx` - Search functionality
- `useUnitPreferences.ts` - Unit conversion
- `useProfile.tsx` - User profile
- `useLocations.ts` - Location data
- `useRideCreditFilters.ts` - Credit filtering

## Step 2: Analyze Hook Pattern

Extract the hook structure:

```tsx
export function useFeature(params: Params) {
  // State
  const [data, setData] = useState<Type>(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<Error | null>(null)

  // Effects
  useEffect(() => {
    fetchData()
  }, [dependency])

  // Actions
  const doSomething = async () => {
    setLoading(true)
    try {
      const result = await api.call()
      setData(result)
    } catch (e) {
      setError(e)
    } finally {
      setLoading(false)
    }
  }

  return { data, loading, error, doSomething }
}
```

## Step 3: Convert to Composable

### Basic Structure
```tsx
// React
export function useFeature() {
  const [value, setValue] = useState('')
  return { value, setValue }
}
```
↓
```typescript
// Vue
export function useFeature() {
  const value = ref('')
  
  function setValue(newValue: string) {
    value.value = newValue
  }
  
  return { value, setValue }
}
```

### State Conversions
```tsx
// React
const [count, setCount] = useState(0)
const [user, setUser] = useState<User | null>(null)
const [items, setItems] = useState<Item[]>([])
```
↓
```typescript
// Vue
const count = ref(0)
const user = ref<User | null>(null)
const items = ref<Item[]>([])
```

### Effect Conversions
```tsx
// React - Run on mount
useEffect(() => {
  initialize()
}, [])
```
↓
```typescript
// Vue
onMounted(() => {
  initialize()
})
```

```tsx
// React - Watch dependency
useEffect(() => {
  fetchData(id)
}, [id])
```
↓
```typescript
// Vue
watch(() => id, (newId) => {
  fetchData(newId)
}, { immediate: true })
```

### Supabase → Django API
```tsx
// React (Supabase)
const { data } = await supabase
  .from('parks')
  .select('*')
  .eq('slug', slug)
  .single()
```
↓
```typescript
// Vue (Django)
const api = useApi()
const { data } = await api<Park>(`/parks/${slug}/`)
```

## Step 4: Handle Complex Patterns

### useCallback → Plain Function
```tsx
// React
const memoizedFn = useCallback(() => {
  doSomething(dep)
}, [dep])
```
↓
```typescript
// Vue - Usually no memo needed
function doSomething() {
  // Vue's reactivity handles this
}
```

### useMemo → computed
```tsx
// React
const derived = useMemo(() => expensiveCalc(data), [data])
```
↓
```typescript
// Vue
const derived = computed(() => expensiveCalc(data.value))
```

### Custom Hook Composition
```tsx
// React
function useFeature() {
  const auth = useAuth()
  const { data } = useQuery(...)
  // ...
}
```
↓
```typescript
// Vue
export function useFeature() {
  const { user } = useAuth()
  const api = useApi()
  // ...
}
```

## Step 5: Target Location

Place composables in:
```
frontend/app/composables/
├── useApi.ts           # Base API client
├── useAuth.ts          # Authentication
├── useParksApi.ts      # Parks API
├── useRidesApi.ts      # Rides API
├── useModeration.ts    # Moderation queue
└── use[Feature].ts     # New composables
```

## Step 6: Verify Parity

- [ ] All returned values present
- [ ] All actions/methods work
- [ ] State updates correctly
- [ ] API calls translated
- [ ] Error handling maintained
- [ ] Loading states work
- [ ] TypeScript types correct

## Priority Hooks to Migrate

| Hook | Size | Complexity |
|------|------|------------|
| useModerationQueue.ts | 21KB | High |
| useEntityVersions.ts | 14KB | High |
| useAuth.tsx | 11KB | Medium |
| useAutoComplete.ts | 10KB | Medium |
| useRateLimitAlerts.ts | 10KB | Medium |
| useRideCreditFilters.ts | 9KB | Medium |
| useAdminSettings.ts | 9KB | Medium |
