← Debugging Guides

阅读中文版本

Vue: destructured props lose reactivity

Fix Vue 3 reactivity loss when destructuring reactive objects or props, and choose between toRefs, toRef, and reactive-aware destructuring.

intermediatedebuggingvuereactivity

Symptoms

  • A prop or reactive field updates in state but the template does not re-render
  • Values captured from a destructured object stay at their initial value
  • Computed properties never recompute after a change

Possible causes

  • Destructuring a reactive() object copies primitives, losing reactivity
  • Destructuring props in setup() takes plain values, not refs
  • Passing a computed value into a plain variable before use
  • Using a primitive from a ref without .value inside a function

Diagnosis steps

Check whether the value is a ref or a plain copy

console.log(isRef(value))

Interpretation

A false result after destructuring confirms reactivity was lost at the destructure.

Convert with toRefs or toRef

Interpretation

toRefs(props) yields refs that stay connected; plain destructuring yields snapshots.

Trace where the template reads the value

Interpretation

Templates unwrap refs automatically, but only if they still read the ref, not a copied primitive.

Root cause

Vue tracks reactivity through object property access and ref identity. Destructuring a reactive object reads the current value once and copies it, breaking the dependency link, so later mutations update the source but not the copied variable. In the Composition API, props destructured in setup() behave the same way.

Fix

Use toRefs(props) or toRef(props, key) to obtain references that remain reactive, or read props directly (props.title) rather than destructuring. For reactive() objects, keep field access on the object or convert to refs with toRefs. In templates, functions should reference a ref via .value or the unwrapped reactive field.

Verify the fix

Update the parent prop and observe; the child template reflects the new value immediately, and isRef on the converted value returns true.

Symptoms

A child component receives updated props but its template keeps showing the old value. Computed properties that depend on the value never recompute.

Diagnosis

Check whether the value is still a ref after destructuring:

import { isRef } from 'vue';

const { title } = defineProps(['title']); // plain value, not a ref
console.log(isRef(title)); // false

Fix

Keep the reference with toRefs:

import { toRefs } from 'vue';

const props = defineProps(['title', 'count']);
const { title, count } = toRefs(props); // reactive refs
// template reads stay reactive; in script use title.value

For reactive objects, access fields on the object or wrap with toRefs.

Verification

Change the parent value and confirm the child re-renders. isRef(title) now returns true, proving the reactivity link survived.

Additional Notes

Reactivity in Vue is tied to property access on a reactive object or to a ref identity, so copying a value out with destructuring reads it once and breaks the link. Converting with the reference helpers preserves that connection and keeps templates updating, while reading through the original object works just as well. The mistake is easy to miss because the initial value displays correctly and only later updates fail to appear, so a quick check of whether the extracted value is still a reference is the fastest way to confirm the diagnosis.