Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 debugging reference for reactivity issues, computed errors, watcher bugs, async failures, and SSR hydration problems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/watcheffect-async-dependency-tracking.md
1---2title: watchEffect Only Tracks Dependencies Before First Await3impact: HIGH4impactDescription: Dependencies accessed after await are not tracked, causing watchers to miss reactive changes5type: capability6tags: [vue3, watchEffect, watchers, async, await, dependency-tracking]7---89# watchEffect Only Tracks Dependencies Before First Await1011**Impact: HIGH** - `watchEffect` automatically tracks reactive dependencies, but only during synchronous execution. Any reactive properties accessed after the first `await` statement will NOT be tracked, and changes to them won't trigger the watcher.1213For async operations, either access all dependencies before the await, or use `watch` with explicit dependencies.1415## Task Checklist1617- [ ] Access all reactive dependencies BEFORE the first await in watchEffect18- [ ] Use `watch` with explicit source when async tracking is needed19- [ ] Store reactive values in local variables before await20- [ ] Be aware that dependencies after await are invisible to Vue2122**Incorrect:**23```vue24<script setup>25import { ref, watchEffect } from 'vue'2627const userId = ref(1)28const includeDetails = ref(true)29const userData = ref(null)3031// BAD: includeDetails is accessed after await - NOT TRACKED!32watchEffect(async () => {33const response = await fetch(`/api/users/${userId.value}`)34const data = await response.json()3536// This dependency is NOT tracked - changes won't trigger re-run37if (includeDetails.value) {38userData.value = { ...data, details: await fetchDetails(data.id) }39} else {40userData.value = data41}42})4344// BAD: Multiple dependencies after await45watchEffect(async () => {46await someAsyncSetup()4748// None of these are tracked!49console.log(optionA.value) // Not tracked50console.log(optionB.value) // Not tracked51doSomething(optionC.value) // Not tracked52})53</script>54```5556**Correct:**57```vue58<script setup>59import { ref, watchEffect, watch } from 'vue'6061const userId = ref(1)62const includeDetails = ref(true)63const userData = ref(null)6465// CORRECT: Access all dependencies before await66watchEffect(async () => {67// Capture reactive values synchronously68const id = userId.value69const withDetails = includeDetails.value7071// Now these are tracked72const response = await fetch(`/api/users/${id}`)73const data = await response.json()7475if (withDetails) {76userData.value = { ...data, details: await fetchDetails(data.id) }77} else {78userData.value = data79}80})8182// ALTERNATIVE: Use watch with explicit dependencies83watch(84[userId, includeDetails],85async ([id, withDetails]) => {86const response = await fetch(`/api/users/${id}`)87const data = await response.json()8889if (withDetails) {90userData.value = { ...data, details: await fetchDetails(data.id) }91} else {92userData.value = data93}94},95{ immediate: true }96)97</script>98```99100## Pattern: Extract Dependencies First101102```vue103<script setup>104import { ref, watchEffect } from 'vue'105106const filters = ref({ status: 'active', sortBy: 'name' })107const page = ref(1)108const results = ref([])109110// CORRECT: Extract all needed values synchronously111watchEffect(async () => {112// All dependencies accessed before await - all tracked!113const { status, sortBy } = filters.value114const currentPage = page.value115116// Now safe to do async work117const response = await fetch(118`/api/items?status=${status}&sort=${sortBy}&page=${currentPage}`119)120results.value = await response.json()121})122</script>123```124125## When to Use watch Instead126127```vue128<script setup>129import { ref, watch } from 'vue'130131const source = ref('initial')132const option = ref('default')133const result = ref(null)134135// BEST for complex async: Use watch with explicit sources136// All dependencies are explicitly declared and always tracked137watch(138[source, option],139async ([sourceVal, optionVal]) => {140const data = await processAsync(sourceVal)141result.value = applyOption(data, optionVal)142},143{ immediate: true }144)145</script>146```147148## Debugging Untracked Dependencies149150```vue151<script setup>152import { ref, watchEffect } from 'vue'153154const a = ref(1)155const b = ref(2)156157watchEffect(async () => {158console.log('Tracked dependency a:', a.value) // Tracked159160await someAsyncOperation()161162console.log('Untracked dependency b:', b.value) // NOT tracked!163// Changing b.value won't re-run this watchEffect164})165166// Test: Change a.value -> watchEffect re-runs167// Test: Change b.value -> watchEffect does NOT re-run168</script>169```170171## Reference172- [Vue.js Watchers - watchEffect](https://vuejs.org/guide/essentials/watchers.html#watcheffect)173- [Vue.js API - watchEffect](https://vuejs.org/api/reactivity-core.html#watcheffect)174