Files
thrillwiki_django_no_react/.agent/workflows/migrate-hook.md
pacnpal 1adba1b804 lol
2026-01-02 07:58:58 -05:00

4.2 KiB

description
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:

/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:

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

// React
export function useFeature() {
  const [value, setValue] = useState('')
  return { value, setValue }
}

// Vue
export function useFeature() {
  const value = ref('')
  
  function setValue(newValue: string) {
    value.value = newValue
  }
  
  return { value, setValue }
}

State Conversions

// React
const [count, setCount] = useState(0)
const [user, setUser] = useState<User | null>(null)
const [items, setItems] = useState<Item[]>([])

// Vue
const count = ref(0)
const user = ref<User | null>(null)
const items = ref<Item[]>([])

Effect Conversions

// React - Run on mount
useEffect(() => {
  initialize()
}, [])

// Vue
onMounted(() => {
  initialize()
})
// React - Watch dependency
useEffect(() => {
  fetchData(id)
}, [id])

// Vue
watch(() => id, (newId) => {
  fetchData(newId)
}, { immediate: true })

Supabase → Django API

// React (Supabase)
const { data } = await supabase
  .from('parks')
  .select('*')
  .eq('slug', slug)
  .single()

// Vue (Django)
const api = useApi()
const { data } = await api<Park>(`/parks/${slug}/`)

Step 4: Handle Complex Patterns

useCallback → Plain Function

// React
const memoizedFn = useCallback(() => {
  doSomething(dep)
}, [dep])

// Vue - Usually no memo needed
function doSomething() {
  // Vue's reactivity handles this
}

useMemo → computed

// React
const derived = useMemo(() => expensiveCalc(data), [data])

// Vue
const derived = computed(() => expensiveCalc(data.value))

Custom Hook Composition

// React
function useFeature() {
  const auth = useAuth()
  const { data } = useQuery(...)
  // ...
}

// 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