What Causes This Error?
This error occurs when you pass something other than a function to callOnce(). The first argument must be a callable function.
The Problem
<script setup>
// ❌ Wrong - passing a value, not a function
callOnce('analytics', analyticsData)
// ❌ Wrong - passing undefined
const handler = undefined
callOnce('setup', handler)
// ❌ Wrong - passing a promise directly
callOnce('data', fetch('/api/data'))
</script>
The Fix
Pass a Function
<script setup>
// ✅ Correct - passing a function
callOnce('analytics', () => {
initAnalytics()
})
// ✅ Correct - async function
callOnce('setup', async () => {
await setupApplication()
})
// ✅ Correct - named function
const initializeApp = () => {
console.log('App initialized')
}
callOnce('init', initializeApp)
</script>
Understanding callOnce
callOnce ensures a function runs only once during the application lifecycle, even with HMR or navigation.
<script setup>
// Runs only once, even if component remounts
callOnce('tracking', () => {
console.log('This runs only once!')
initializeTracking()
})
</script>
Common Patterns
Analytics Initialization
<script setup>
// ✅ Correct - wrap initialization in function
callOnce('analytics', () => {
if (process.client) {
gtag('config', 'GA_MEASUREMENT_ID')
}
})
</script>
One-time API Call
<script setup>
// ✅ Correct - async function for data fetching
const config = ref(null)
callOnce('load-config', async () => {
const data = await $fetch('/api/config')
config.value = data
})
</script>
Third-party SDK
<script setup>
// ✅ Correct - initialize SDK once
callOnce('chat-widget', () => {
if (process.client) {
loadIntercom({
app_id: 'YOUR_APP_ID'
})
}
})
</script>
With Key Parameter
<script setup>
const userId = computed(() => user.value?.id)
// ✅ Function with unique key per user
callOnce(`user-setup-${userId.value}`, () => {
setupUserPreferences(userId.value)
})
</script>
Error Scenarios
Variable That Might Be Undefined
<script setup>
// ❌ Handler might be undefined based on condition
const handler = someCondition ? initHandler : undefined
callOnce('init', handler) // Error if undefined!
// ✅ Always provide a function
callOnce('init', () => {
if (someCondition) {
initHandler()
}
})
</script>
Accidental Value Instead of Function
<script setup>
const result = computeSomething() // Returns a value
// ❌ Wrong - passing the result, not a function
callOnce('compute', result)
// ✅ Correct - wrap in function
callOnce('compute', () => {
return computeSomething()
})
</script>
Promise Instead of Async Function
<script setup>
// ❌ Wrong - passing a promise
callOnce('fetch', fetch('/api/data'))
// ✅ Correct - async function that returns promise
callOnce('fetch', async () => {
return await fetch('/api/data')
})
</script>
Async callOnce
<script setup>
// ✅ Can await callOnce with async function
await callOnce('setup', async () => {
const config = await $fetch('/api/config')
applyConfig(config)
})
// Code here runs after setup is complete
console.log('Setup done')
</script>
Conditional Execution Inside
<script setup>
// ✅ Put conditions inside the function
callOnce('conditional-init', () => {
if (process.client && someFeatureEnabled) {
initFeature()
}
})
// ❌ Don't conditionally call callOnce (might skip registration)
if (someFeatureEnabled) {
callOnce('init', initFeature) // Inconsistent behavior!
}
</script>
TypeScript
<script setup lang="ts">
// ✅ Type-safe callOnce
callOnce<void>('analytics', () => {
initAnalytics()
})
// ✅ With return type
const result = await callOnce<{ status: string }>('status', async () => {
return { status: 'initialized' }
})
</script>
Difference from Other Patterns
vs onMounted
<script setup>
// onMounted - runs every time component mounts
onMounted(() => {
console.log('Runs on every mount')
})
// callOnce - runs only once ever
callOnce('once', () => {
console.log('Runs only once')
})
</script>
vs Regular Function Call
<script setup>
// Regular call - runs on every navigation/HMR
initAnalytics() // Might run multiple times
// callOnce - guaranteed single execution
callOnce('analytics', () => {
initAnalytics() // Runs once
})
</script>
Debugging
<script setup>
callOnce('debug-test', () => {
console.log('callOnce executed at:', new Date().toISOString())
return 'completed'
})
</script>
Quick Checklist
- First argument is a function, not a value
- Use arrow function or named function
- Wrap async operations in async function
- Don’t pass undefined or null
- Put conditions inside the function, not outside
- Use unique key for different call contexts