This is the setup:
Parent component: Has a reactive Todo object with a checked state {checked: true
and passes it to a Todo
component.
Todo component: Accepts the Todo as a prop and binds the checked
value to a checkbox. Upon toggling the checkbox, it emits an event to the parent, which then asynchronously updates the checked
state of the Todo.
Parent:
<template>
<Todo @update="handleUpdate" :todo="todo" />
</template>
<script setup>
import { reactive } from "vue";
import Todo from "./components/Todo.vue";
let todo = reactive({ checked: true });
let handleUpdate = (e) => {
setTimeout(() => {
// Always sets it to false for test purposes
todo.checked = false;
}, 1000);
};
</script>
Child:
<template>
<input type="checkbox" v-model="checkboxProxy" />
</template>
<script setup>
const { computed, defineProps, defineEmits } = require("vue");
let props = defineProps(["todo"]);
let emit = defineEmits(["update"]);
let checkboxProxy = computed({
get() {
return props.todo.checked;
},
set(val) {
emit("update", val);
},
});
</script>
Question: I would expect the checkbox to get unchecked a second after it has been checked. But that doesn't happen. Why?
Codesandbox: https://codesandbox.io/s/crazy-snow-kdse27
I also tried this setup:
<input type="checkbox" :checked="todo.checked" @change="emit('update)" />
But it still not reflecting the todo state. I'm testing in Brave it that matters.
CodePudding user response:
You are not changing the todo
object itself but just the HTML element. Meaning, once the state is set to false it is not updated to true when you emit the event.
And because you use reactive and computed the state is cached, so nothing will happen until something is changed.
let handleUpdate = (e) => {
// actually changing the state here
todo.checked = true;
setTimeout(() => {
// Always sets it to false for test purposes
todo.checked = false;
}, 1000);
};
todo.checked = true
is going to change the state and after one second (in your case) it is going to be set back to false, which respectively will update the props and the checkbox itself.
Updated:
To update the checkbox based on the response you can either:
- prevent the default action and update if needed (when the response is ready).
Todo.vue
<div>
<input type="checkbox" :checked="props.todo.checked" @click.prevent="emit('update', $event.target.value)" />
<div>Prop value: {{ todo }}</div>
</div>
- Update the state before the response, and then set the data from the response if there is a change as in the example from above.
If I'm not mistaken, using Vue.set()
in vue 2 actually will result in the same (visual) behavior as pt2
Keep in mind that both ways are not very UX-friendly.