Reputation: 11600
Consider this illustrative example:
const App = {
setup() {
const name = Vue.ref("");
let firstTime = true;
const message = Vue.computed(() => {
if (firstTime) {
firstTime = false;
return "Welcome stranger";
}
return `Hello ${name.value}`;
});
return {
name,
message
}
}
};
Vue.createApp(App).mount("#root");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name"/> <br/>
message: {{ message }}
</div>
As you can see, message
stores a computed value that should track updates to name
but it isn't.
Why is it like that and how to fix it?
Upvotes: 7
Views: 10888
Reputation: 11600
In this particular example when different results are expected on initial render we can use watch
instead of computed
since watch
is lazy by default and won't be executed on initial render (only when its dependency: name
changes) and that's exactly what is needed here.
const App = {
setup() {
const name = Vue.ref("");
Vue.watch(name, () => state.message = `Hello ${name.value}`);
const state = {
name,
message: "Welcome stranger"
};
return state;
}
};
Vue.createApp(App).mount("#root");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name" /> <br/> message: {{ message }}
</div>
Upvotes: 2
Reputation: 3543
This is because Vue cannot discover the dependency between your computed property message
and the ref name
if you write it this way. The problem is the firstTime
variable.
What is going on is that Vue discovers dependency at runtime (instead of compile time) by running the computed property and observing what reactive references are accessed during the process:
name.value
is not accessed the first time your computed property runs. And because during the first run no reactive ref is accessed at all, your computed property will never be triggered again.It is fine if you don't access the name.value for the first time, but you need to access something else that is reactive and will change when firstTime becomes false:
const App = {
setup() {
const name = Vue.ref("");
const firstTime = Vue.ref(true);
Vue.watch(()=> name.value, ()=>{firstTime.value=false;});
const message = Vue.computed(() => {
if (firstTime.value) {
return "Welcome stranger";
}
return `Hello ${name.value}`;
});
return {
name,
message
}
}
};
Vue.createApp(App).mount("#root");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name"/> <br/>
message: {{ message }}
</div>
Upvotes: 5
Reputation: 1
Assign name.value
to a variable in the beginning of computed property then return it at the end
const App = {
setup() {
const name = Vue.ref("");
let firstTime =true
const message = Vue.computed(() => {
let _name=name.value
if (firstTime) {
firstTime= false;
return "Welcome stranger";
}
return `Hello ${_name}`;
});
return {
name,
message
}
}
};
Vue.createApp(App).mount("#root");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name" /> <br/> message: {{ message }} <br/> name:{{name}}
</div>
Upvotes: 3
Reputation: 1124
Computed should always use an immutable reactive ref object you want to be computed.
so if you declare the reactive objects you are using at the beginning it will work.
const App = {
setup() {
const name = Vue.ref("");
let firstTime = true;
const message = Vue.computed(() => {
name.value;
if (firstTime) {
firstTime = false;
return "Welcome stranger";
}
return `Hello ${name.value}`;
});
return {
name,
message
}
}
};
Vue.createApp(App).mount("#root");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name"/> <br/>
message: {{ message }}
</div>
Upvotes: 7