Vue watchEffect vs watch
April 10, 2025 · AI Conversations
—
### `watchEffect`
– **Use Case**: Automatically tracks **all reactive dependencies** used inside its callback and re-runs when any of them change.
– **Usage**:
import { watchEffect } from 'vue';
watchEffect(() => {
console.log(myVar.value); // automatically tracks this
});
– **Pros**:
– No need to manually specify dependencies.
– Great for simple side effects that depend on multiple reactive sources.
– **Cons**:
– Less control—can re-run more often than needed.
– Can be hard to debug if many dependencies are accessed inside.
—
### `watch`
– **Use Case**: Tracks a **specific** reactive source or sources.
– **Usage**:
import { watch } from 'vue';
watch(myVar, (newVal, oldVal) => {
console.log('Changed from', oldVal, 'to', newVal);
});
– **Pros**:
– More control over what to watch.
– Provides access to old and new values.
– You can set options like `immediate`, `deep`, `flush`.
– **Cons**:
– You must explicitly declare dependencies.
—
### Summary Table:
| Feature | `watchEffect` | `watch` |
|—————–|—————————————-|——————————————|
| Dependency tracking | Automatic | Manual |
| Access to old value | No | Yes |
| Fine-grained control | Less | More |
| Suitable for | Quick side-effects | Watching specific value changes |
| Lazy execution | No (runs immediately) | Yes (can be lazy unless `immediate: true`) |
—
### When to use what?
– Use **`watchEffect`** when you just need to react to reactive state changes without caring which one changed.
– Use **`watch`** when you want precise control, like comparing old and new values, or deep-watching an object.
Let me know if you want real-life examples too.
—
### Setup:
We simulate fetching commits from GitHub API when `repoName` changes.
<script setup>
import { ref, watch, watchEffect } from 'vue';
import axios from 'axios';
const repoName = ref('vuejs/core'); // format: owner/repo
const commits = ref([]);
const isLoading = ref(false);
// Using watch
watch(repoName, async (newRepo) => {
isLoading.value = true;
try {
const res = await axios.get(`https://api.github.com/repos/${newRepo}/commits`);
commits.value = res.data.map(c => c.commit.message);
} catch (error) {
commits.value = ['Error fetching commits'];
} finally {
isLoading.value = false;
}
});
// Using watchEffect (for demo: also fetch on initial load)
watchEffect(async () => {
if (!repoName.value) return;
isLoading.value = true;
try {
const res = await axios.get(`https://api.github.com/repos/${repoName.value}/commits`);
commits.value = res.data.map(c => c.commit.message);
} catch (error) {
commits.value = ['Error fetching commits'];
} finally {
isLoading.value = false;
}
});
</script>
<template>
<div>
<input v-model="repoName" placeholder="Enter repo (e.g., vuejs/core)" />
<p v-if="isLoading">Loading...</p>
<ul v-else>
<li v-for="(msg, index) in commits" :key="index">{{ msg }}</li>
</ul>
</div>
</template>
—
### Notes:
– The **`watch`** is triggered when `repoName` changes.
– The **`watchEffect`** runs immediately and again whenever `repoName` changes (so yes, in this case it duplicates the call unless you remove one).
– In real apps, you'd choose one depending on need—`watch` for old vs new comparison, `watchEffect` for quick reactive side effects.
Want a version using just one (e.g., only `watch`) or more optimized with debounce?
—
### `mounted()`
– **What it is**: A **lifecycle hook**.
– **When it runs**: Once **after the component is mounted** to the DOM.
– **Use case**: Ideal for running code that interacts with the DOM or needs to run once, like API calls on initial load.
**Example:**
<script setup>
import { onMounted } from 'vue';
onMounted(() => {
console.log('Component mounted!');
fetchData();
});
</script>
—
### `watchEffect()`
– **What it is**: A **reactivity tracking function**.
– **When it runs**: Immediately on setup, and **re-runs** whenever any reactive dependency used inside it changes.
– **Use case**: Useful for tracking reactive state and running side effects **automatically** based on those changes.
**Example:**
<script setup>
import { ref, watchEffect } from 'vue';
const count = ref(0);
watchEffect(() => {
console.log('Count is:', count.value); // runs once and again every time count changes
});
</script>
—
### Summary Table
| Feature | `mounted()` | `watchEffect()` |
|———————–|————————————|——————————————|
| Type | Lifecycle Hook | Reactive Side Effect |
| Runs when | Once after DOM is mounted | Immediately, then on any dependency change |
| Reactivity awareness | No | Yes |
| Typical use cases | Fetch data, set up DOM, plugins | React to reactive state automatically |
| Runs more than once? | No | Yes |
—
### When to Use:
– Use **`mounted()`** when you want to do something **once**, like:
– Fetching initial data
– Starting a library
– Accessing `$refs`
– Use **`watchEffect()`** when you want to **automatically react to reactive changes**, like:
– Watching `route` changes
– Updating something when a `ref` or `computed` changes
—
Let me know if you want an example comparing both in the same component.
—
### Example: Fetch GitHub Repo Info on Mount
<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
const repoName = ref('vuejs/vue')
const repoInfo = ref(null)
const isLoading = ref(true)
const error = ref('')
onMounted(async () => {
try {
const response = await axios.get(`https://api.github.com/repos/${repoName.value}`)
repoInfo.value = response.data
} catch (err) {
error.value = 'Failed to fetch repo info.'
} finally {
isLoading.value = false
}
})
</script>
<template>
<div>
<h2>GitHub Repo Info</h2>
<p v-if="isLoading">Loading...</p>
<p v-if="error">{{ error }}</p>
<div v-if="repoInfo">
<p><strong>Name:</strong> {{ repoInfo.name }}</p>
<p><strong>Stars:</strong> {{ repoInfo.stargazers_count }}</p>
<p><strong>Description:</strong> {{ repoInfo.description }}</p>
</div>
</div>
</template>
—
### Why Use `onMounted` Here?
– **Runs once**: You only want to fetch the data **once**, when the component is loaded.
– **Avoid repeated calls**: You don’t want it to rerun on reactive changes like `repoName` unless you explicitly choose to.
– **DOM ready**: Useful if the logic depends on something being in the DOM (e.g., `$refs`, third-party libraries, or layout calculations).
—
### When *not* to use `onMounted`:
– When you want something to reactively update based on `ref` or `prop` changes (for that, use `watch` or `watchEffect`).
Let me know if you want a comparison with `watchEffect` for this use case!