Either I have a basic misunderstanding of Vue.js or I have found a bug. I am able to reproduce this bug (?) in the minimal example below.
The component App.vue
has a reactive message
variable, which is passed down to the child Child.vue
as a prop. This message can be changed with a button. This works fine. However, when you alert the message in the Child, you will always get the first message - it is not updated at all. It seems that the message is only updated in the markup, not in the JavaScript. I am really confused by this behavior.
<script setup>
import { ref, computed } from "vue";
import Child from "./components/Child.vue";
let index = ref(0);
let messages = ["hi", "there", "this", "is", "strange"];
let message = computed(() => messages[index.value]);
function change_message() {
index.value++;
if (index.value === messages.length) {
index.value = 0;
}
}
</script>
<template>
<Child :message="message"></Child>
<div>
<button @click="change_message">Change message</button>
</div>
</template>
<script setup>
const { message } = defineProps(["message"]);
function alert_message() {
window.alert(message);
}
</script>
<template>
<h1>
{{ message }}
</h1>
<button @click="alert_message">Alert message</button>
</template>
There is a workaround: Do not destructure the props in the Child. So write const props = defineProps(["message"]);
and then use window.alert(props.message);
. However, in a real-world Vue application, this will result in quite bloated code. I do not want to carry the props.
around all the time.
When you destructure the props, you are loosing reactivity, For your case one way could be use toRefs:
Child.vue
<script setup>
import { toRefs } from "vue";
const props = defineProps(["message"]);
const { message } = toRefs(props);
function alert_message() {
window.alert(message.value);
}
</script>
<template>
<h1>
{{ message }}
</h1>
<button @click="alert_message()">Alert message</button>
</template>
https://codesandbox.io/s/nameless-water-jwhw90?file=/src/components/Child.vue
Anyway is an standard to use props.<your_prop>
when it's required, even for in a real-world Vue apps