What Causes This Warning?
This warning appears when you pass the execute function from useFetch or useAsyncData directly to Vue’s watch(). This creates issues because execute can trigger multiple unwanted fetches.
The Problem
<script setup>
const { data, execute } = useFetch('/api/users')
// ❌ Wrong - passing execute directly to watch
watch(someRef, execute)
// ❌ Wrong - also incorrect
watch(() => userId.value, execute)
</script>
The Fix
Use refresh Instead
<script setup>
const { data, refresh } = useFetch('/api/users')
// ✅ Correct - use refresh
watch(someRef, () => {
refresh()
})
</script>
Or Call Function Explicitly
<script setup>
const { data, execute } = useFetch('/api/users', { immediate: false })
// ✅ Correct - wrap in a function
watch(someRef, () => {
execute()
})
</script>
Understanding execute vs refresh
| Method | Purpose | When to Use |
|---|---|---|
execute() | Run the fetch (respects immediate option) | Initial fetch, manual triggers |
refresh() | Re-run the fetch (ignores immediate) | Reactively update data |
<script setup>
const { data, execute, refresh, pending } = useFetch('/api/data', {
immediate: false // Don't fetch on mount
})
// Initial fetch
onMounted(() => {
execute() // Runs the fetch
})
// Reactive updates
watch(filters, () => {
refresh() // Re-fetches when filters change
})
</script>
Common Patterns
Watch with Computed URL
<script setup>
const userId = ref(1)
// ✅ Best approach - let useFetch handle reactivity
const { data, refresh } = useFetch(() => `/api/users/${userId.value}`)
// URL changes automatically trigger refetch
// No watch needed!
</script>
Manual Refresh on Dependency Change
<script setup>
const category = ref('electronics')
const sortBy = ref('price')
const { data, refresh } = useFetch('/api/products', {
query: {
category: category,
sort: sortBy
}
})
// ✅ Use refresh if you need manual control
watch([category, sortBy], () => {
refresh()
})
</script>
With useAsyncData
<script setup>
const page = ref(1)
const { data, refresh } = await useAsyncData(
'products',
() => $fetch('/api/products', { query: { page: page.value } })
)
// ✅ Correct - use refresh in watch
watch(page, () => {
refresh()
})
// Or use the key for automatic refresh
const { data: autoData } = await useAsyncData(
() => `products-${page.value}`, // Key changes, triggers refetch
() => $fetch('/api/products', { query: { page: page.value } })
)
</script>
Debounced Refresh
<script setup>
import { useDebounceFn } from '@vueuse/core'
const searchQuery = ref('')
const { data, refresh } = useFetch(() => `/api/search?q=${searchQuery.value}`)
// ✅ Debounced refresh
const debouncedRefresh = useDebounceFn(() => {
refresh()
}, 300)
watch(searchQuery, () => {
debouncedRefresh()
})
</script>
Conditional Refresh
<script setup>
const userId = ref(null)
const { data, refresh } = useFetch(() => `/api/users/${userId.value}`, {
immediate: false // Don't fetch until we have a userId
})
// ✅ Only refresh when userId is valid
watch(userId, (newId) => {
if (newId) {
refresh()
}
})
</script>
Alternative: watch Option
Use the built-in watch option instead of manual watching:
<script setup>
const category = ref('all')
// ✅ Built-in watch option
const { data } = useFetch('/api/products', {
query: { category },
watch: [category] // Automatically refreshes when category changes
})
</script>
<script setup>
const filters = reactive({
category: 'all',
minPrice: 0,
maxPrice: 1000
})
// ✅ Watch multiple values
const { data } = useFetch('/api/products', {
query: filters,
watch: [() => filters.category, () => filters.minPrice]
})
</script>
Deep Watch
<script setup>
const filters = ref({ nested: { value: 1 } })
// ✅ Deep watch with refresh
watch(
filters,
() => { refresh() },
{ deep: true }
)
// Or use the watch option with deep
const { data } = useFetch('/api/data', {
query: filters,
watch: [filters],
deep: true
})
</script>
With Transform
<script setup>
const type = ref('active')
const { data, refresh } = useFetch('/api/items', {
query: { type },
transform: (items) => items.filter(i => i.visible)
})
// ✅ Transform runs again on refresh
watch(type, () => {
refresh()
})
</script>
Quick Checklist
- Don’t pass
executedirectly towatch() - Use
refresh()for reactive updates - Wrap
execute()in a function if needed - Consider using the built-in
watchoption - Use computed URLs for automatic reactivity
- Add debouncing for frequently changing values