Calling child component functions
Three distinct methods to call child component functions on its parent component.
Catherine Ourique
Posted on June 14, 2024
In this post, I will demonstrate three different methods for calling child component functions on its parent while also highlighting the advantages and disadvantages of each one from my point of view.
This is one of the easiest methods to call child component functions. If you assign a reference to a child component, you can have basically full access to its properties, so you can easily call its functions. See the example below.
Parent.vue
<template>
<div>
<Children ref="childrenReference" />
<button @click="runHit"> Parent Button </button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const childrenReference = ref(null);
const runHit = () => {
childrenReference.value.hit()
}
</script>
Children.vue
<template>
<div
{{ counter }}
</div>
</template>
<script setup>
import { ref } from 'vue'
const counter = ref(0);
const hit = () => {
if (counter.value >= 9) {
counter.value = 0;
} else {
counter.value += 1;
}
};
defineExpose({
hit,
});
</script>
Here is the result of this implementation, with some Vuetify components to make it look pretty.
So let's talk about the main advantages of this method:
Easy to implement (even easier with Options API);
Control over which methods are exposed (Composition API only);
Is the approach taken by Vuetify (I really love Vuetify);
And now the disadvantages:
Requires defining a reference for your child component;
With Options API, you have full access to private component data;
With Options API, you have full access to private component data;
I particularly don't like this method, but well, it works. Let me show you an example.
Parent.vue
<template>
<div>
<Children :runHit="runHitValue" />
<button @click="runHit"> Parent Button </button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const runHitValue = ref(null);
const runHit = () => {
runHitValue.value = crypto.randomUUID();
};
</script>
Children.vue
<template>
<div
{{ counter }}
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const props = defineProps({
runHit: {
type: String,
required: false,
},
});
const counter = ref(0);
const hit = () => {
if (counter.value >= 9) {
counter.value = 0;
} else {
counter.value += 1;
}
};
watch(
() => props.runHit,
() => {
hit();
},
);
</script>
And as shown in the previous method, you can see in the example below that it works.
The advantages of this method are:
It works (Does it count as an advantage or just as the bare minimum?);
But for this one, we have a lot of disadvantages:
Requires declaring props on child component for each function;
Need to create a random arbitrary reactive variable on the parent to trigger the child function;
Each function needs to be attached to a different watcher which is associated with an exclusive prop;
Very hard to read and maintain;
Vue emits is one of my favorite features. This one can be kind of weird at first glance since it depends on the mount order of Vue components. Take some time to digest what is happening, and you will see that it makes total sense.
Parent.vue
<template>
<div>
<Children ref="childrenReference" />
<button @click="runHit"> from-emit-parent-button </button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const childrenReference = ref(null);
const runHit = () => {
childrenReference.value.hit()
}
</script>
Children.vue
<template>
<div
{{ counter }}
</div>
</template>
<script setup>
import { ref } from 'vue'
const counter = ref(0);
const hit = () => {
if (counter.value >= 9) {
counter.value = 0;
} else {
counter.value += 1;
}
};
defineExpose({
hit,
});
</script>
Now here's a working example of this approach.
Let's talk about the advantages we have with this method:
In Options API, it is a good way to maintain data privacy in child components;
Multiple function handlers can be emitted at once;
Uses Vue's native children-to-parent component communication;
Does not depend on reference access;
Easy to know all available methods;
And the disadvantages:
Kinda odd to understand what is happening under the hood;
Always depends on component mount order;
Depends on reactive variables to store the child functions;
Calling functions from child components is very useful, but it can make your code very hard to understand or maintain if an unusual approach is taken.
Personally, I would avoid the "watched variabled" method at all costs. It works, but compared to the alternatives, it is not a good method.
For the other two methods, I would choose the "emitted function" method if I'm using Vue2 or Options API since it provides better control over child components. For Vue3 or Composition API, the "from reference" method looks very readable and easy to maintain, making it a good choice for implementing across project components.