mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-02-05 13:15:17 -05:00
4.2 KiB
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 stateuseModerationQueue.ts- Moderation logic (21KB)useEntityVersions.ts- Version history (14KB)useSearch.tsx- Search functionalityuseUnitPreferences.ts- Unit conversionuseProfile.tsx- User profileuseLocations.ts- Location datauseRideCreditFilters.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 |