Skip to content

noVueRefObjectReactivityLoss (JavaScript)

Language JavaScript (and super languages)
biome.json
{
"linter": {
"rules": {
"nursery": {
"noVueRefObjectReactivityLoss": "error"
}
}
}
}

Disallow reading the value of a Vue ref in a place where the read runs only once.

A ref is a Vue object that holds a value that can change, such as the result of ref(), shallowRef(), computed(), customRef(), toRef(), toRefs(), useTemplateRef(), or defineModel(). Its current value is stored in its .value property.

Reading .value gives you a copy of the value at that moment. When the read happens in the same function that created the ref, for example directly inside <script setup> or setup(), it runs only once. The copy keeps its first value and does not update when the ref changes later. This is called losing reactivity.

The rule reports these reads of a ref’s value:

  • reading .value, as in const total = count.value;
  • destructuring value, as in const { value } = count.

Reads inside a nested function, such as a computed() getter, a watch() callback, or an event handler, are allowed, because Vue runs that function again when the ref changes. A function that is called right where it is defined, such as (() => { ... })(), runs right away, so reads inside it are reported too. Async functions and generator functions called this way are not reported.

Writing to .value, as in count.value = 1 or count.value++, is allowed.

import { ref } from "vue";
const count = ref(0);
const doubled = count.value * 2;
code-block.js:4:17 lint/nursery/noVueRefObjectReactivityLoss ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This reads the value of a ref only once, so it won't update when the ref changes.

3 │ const count = ref(0);
> 4 │ const doubled = count.value * 2;
│ ^^^^^^^^^^^
5 │

ℹ This code runs only once, together with the code that creates the ref. The value read here is a copy, and Vue does not update it when the ref changes.

ℹ Read .value inside a function, such as a computed() getter, or pass the ref itself instead of its value.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

import { ref } from "vue";
const count = ref(0);
const { value } = count;
code-block.js:4:7 lint/nursery/noVueRefObjectReactivityLoss ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This reads the value of a ref only once, so it won't update when the ref changes.

3 │ const count = ref(0);
> 4 │ const { value } = count;
│ ^^^^^^^^^
5 │

ℹ This code runs only once, together with the code that creates the ref. The value read here is a copy, and Vue does not update it when the ref changes.

ℹ Read .value inside a function, such as a computed() getter, or pass the ref itself instead of its value.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

import { toRefs } from "vue";
const { title } = toRefs(props);
console.log(title.value);
code-block.js:4:13 lint/nursery/noVueRefObjectReactivityLoss ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This reads the value of a ref only once, so it won't update when the ref changes.

3 │ const { title } = toRefs(props);
> 4 │ console.log(title.value);
│ ^^^^^^^^^^^
5 │

ℹ This code runs only once, together with the code that creates the ref. The value read here is a copy, and Vue does not update it when the ref changes.

ℹ Read .value inside a function, such as a computed() getter, or pass the ref itself instead of its value.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

import { computed, ref, watch } from "vue";
const count = ref(0);
const doubled = computed(() => count.value * 2);
watch(count, () => {
console.log(count.value);
});
function increment() {
count.value++;
}
count.value = 10;