JAVASCRIPT

Reactively Perform Actions on Vue 3 Prop or Data Changes with `watch`

Leverage Vue 3's `watch` API to execute side effects, perform complex calculations, or trigger actions in response to specific prop or reactive data changes.

// ProductDetails.vue
<template>
  <div>
    <h2>Product: {{ product.name }}</h2>
    <p>ID: {{ product.id }}</p>
    <p>Price: ${{ product.price }}</p>
    <p v-if="loadingMessage">Loading related products...</p>
    <div v-if="relatedProducts.length">
      <h3>Related Products:</h3>
      <ul>
        <li v-for="rp in relatedProducts" :key="rp.id">{{ rp.name }}</li>
      </ul>
    </div>
    <p v-else-if="!loadingMessage">No related products found.</p>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue';

const props = defineProps({
  product: {
    type: Object,
    required: true,
    validator: (value) => value && value.id && value.name
  }
});

const relatedProducts = ref([]);
const loadingMessage = ref('');

// Watch for changes in props.product.id
watch(
  () => props.product.id,
  async (newId, oldId) => {
    if (newId !== oldId) { // Only fetch if ID actually changes
      loadingMessage.value = `Fetching related products for ID: ${newId}`;
      relatedProducts.value = []; // Clear previous related products
      console.log(`Fetching related products for product ID: ${newId}`);
      try {
        // Simulate an API call
        const response = await new Promise(resolve => setTimeout(() => {
          const data = {
            1: [{ id: 101, name: 'Related Product A' }, { id: 102, name: 'Related Product B' }],
            2: [{ id: 201, name: 'Related Product X' }]
          };
          resolve(data[newId] || []);
        }, 1000));
        relatedProducts.value = response;
      } catch (error) {
        console.error('Failed to fetch related products:', error);
        relatedProducts.value = [];
      } finally {
        loadingMessage.value = '';
      }
    }
  },
  { immediate: true, deep: false } // `immediate` to run on initial render, `deep` not needed for primitive ID
);
</script>

// App.vue (Parent Component)
<template>
  <button @click="changeProduct">Change Product</button>
  <ProductDetails :product="currentProduct" />
</template>

<script setup>
import { ref } from 'vue';
import ProductDetails from './ProductDetails.vue';

const products = [
  { id: 1, name: 'Laptop', price: 1200 },
  { id: 2, name: 'Mouse', price: 25 },
  { id: 3, name: 'Keyboard', price: 75 },
];

const currentProductIndex = ref(0);
const currentProduct = ref(products[currentProductIndex.value]);

function changeProduct() {
  currentProductIndex.value = (currentProductIndex.value + 1) % products.length;
  currentProduct.value = products[currentProductIndex.value];
}
</script>
How it works: This snippet demonstrates the reactive `watch` function in Vue 3 for executing side effects based on changes to a prop. Here, `watch` monitors `props.product.id`. When the product ID changes, an asynchronous operation (simulated API call) is triggered to fetch related products. The `immediate: true` option ensures the watcher runs once on component setup to fetch initial data. This pattern is ideal for scenarios where you need to react to changes in specific data, perform cleanup, or synchronize with external systems, ensuring that expensive operations are only run when necessary.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs