Vue.js Patterns: Using Vue.js 3 Composition API for Reactive Parent to Child Communication
Published: 2023-09-21 18:26:10

---
title: Vue.js Patterns: Using Vue.js 3 Composition API for Reactive Parent to Child Communication
published: true
date: 2023-09-13 00:00:00 UTC
tags: 
canonical_url: https://lirantal.com/blog/vuejs-patterns-using-vuejs-3-composition-api-for-reactive-parent-to-child-communication/
cover_image: https://www.lirantal.com/assets/photo-1505579962197-df174377e13f.a78d83f5_1JcIjV.avif
---

Here’s the use-case: A parent Vue.js component needs to pass data to a child component. It does so using `Props`. The child component needs to receive this external data as its initial state, but also be able to mutate it internally, e.g: binding two-way data for a text input element.

The limitation we face while implementing that are as follows:

- A `prop` is designed to be immutable. The child component shouldn’t mutate the data it receives from the parent component.
- `Ref`s are mutable and allow you to set initial data such as `const content = ref(props.content)`, but depending how you use them, could end up as a pitfall.

## What is the issue with using Vue.js’s `Ref`s?

Refs are part of the Vue.js reactivity system. They are used to create reactive data and they are a proper building block of the Vue.js 3 Composition API.

However, the way you might be using them, per most of the guides I’ve seen, is not going to be helpful with defined use-case.

Consider the following code of a parent component:

```
<template>
  <child-component :content="content"></child-component>
</template>
```

In the child component, you might be looking at using refs by binding them to an initial value that originates from the parent component’s props like so:

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

defineProps({
  content: {
    type: String,
    default: ''
  }
});

const content = ref(props.content);
</script>
```

This is a valid code and will technically work but you’d have to be aware of Vue.js’s lifecycle hooks to understand why it’s not the best approach.

In this component `setup` convention, the `ref()` call would only be ever called once - when the component is mounted. This means that if the parent component changes the `content` prop, the child component will not be aware of that change.

## How to use `Ref`s properly? `watch()` to the rescue!

The solution to this problem is to use the `watch()` function, or its smarter sibling: `watchEffect()`. Like Refs, the watch functions are a part of the Vue.js reactivity system and allow you to watch for changes in a reactive object changes, regardless of the component’s lifecycle hooks.

Following is a complete code example.

We’ll show how to to pass default, initial values, from a parent component to a child component and set these as values using Vuetify 3 and Vue Composition API, while also enabling two-way binding.

The parent component is defined as follows, with a `content` prop that is passed to the child component and may be dynamically changed from the parent component based on its own state:

```
<template>
  <child-component :content="content"></child-component>
</template>
```

In the child component, we’ll define a text input element that will be bound to a content ref with Vue.js’s own `v-model` directive. This will allow us to mutate the value of the `content` ref from the child component, which achieves the two-way binding we’re looking for.

Also allowing the parent component to change the value of the `content` prop, which will be reflected in the child component.

```
<template>
    <v-text-field v-model="content" />
</template>

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

defineProps({
  content: {
    type: String,
    default: ''
  }
});

const content = ref(props.content);
</script>
```

To allow the child component to be aware of changes in the props passed to it we’ll use the `watchEffect()` function to watch for changes in the `content` prop. This will allow us to update the value of the `content` ref, which will be reflected in the child component.

Add the following code to the child component in the global scope next to the `content` variable definition:

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

watch(
  () => props.content,
  () => { content.value = props.content }
);
</script>
```

Let’s explain what is going on here with the `watch()` function.

The `watch` function you see here is part of Vue’s Composition API. Its purpose is to observe and react to changes on a specific reactive source. It could be a simple reactive reference (like a `ref`), or it could be a function that returns a reactive source.

Here’s how it works:

- `() => props.content`: This is called the “source” function. It should return the value that you want to watch. In this case, it’s the `content` value passed from the props.

- `() => { content.value = props.content }`: This is the callback function that gets executed when the source changes. In this case, it assigned the `props.content` value to the `content` ref.

You may also pass an additional 3rd argument options object such as `{ immediate: true }` which means the callback should be run immediately after the `watch` gets created, even before the source has changed. This is useful for cases where you want the callback to run initially when setting up the `watch`.

We can instead use the `watchEffect()` function is a less verbose way to achieve the same result. The `watchEffect()` function will be called every time the `content` prop hydrates, and will update the value of the `content` ref, which will be reflected in the child component.

```
<script setup>
import { watchEffect } from 'vue';

 watchEffect(() => {
  content.value = props.content;
});
</script>
```

### Bonus: using this pattern without the `setup` syntactic sugar

Here’s a generic re-write to the above, but without using the `setup` script convention, demonstrating how our use-case can be achieved using Vue 3’s Composition API:

```
import { ref, watchEffect } from 'vue';

export default {
  props: {
    initialData: {
      type: Array, // change this based on your data type
      default: () => ([]), // provide a default value
    },
  },
  setup(props) {
    // Make a local copy of the received prop
    const data = ref([...props.initialData]);

    // Update local data whenever the prop changes
    watchEffect(() => {
      data.value = [...props.initialData];
    });

    // Now you can manipulate `data` within your component

    return {
      data
    };
  }
};
```

In this example, `initialData` is a prop passed from the parent component. The `data` reactive variable is a local copy of `initialData`. The `watchEffect` function is used to update `data` whenever `initialData` changes, ensuring `data` always starts with the current value of `initialData`.

Since `data` is a local copy, it can be freely mutated inside the child component without affecting the prop’s value in the parent component.