DEV Community

Cover image for Destructuring Vue.js props: The Reactivity Challenge
richardev
richardev

Posted on • Originally published at Medium

Destructuring Vue.js props: The Reactivity Challenge

Destructuring objects is a common practice in JavaScript, and it can make your code cleaner by extracting specific properties. In Vue.js, however, destructuring props can unintentionally break reactivity.


The Pitfalls of Destructured Props

⚡ You cannot destructure defineProps because the resulting destructured variables are not reactive and will not update. Read more

When you destructure props in Vue.js, the resulting variables become plain JavaScript objects. These objects are not reactive, meaning changes to the original props won't trigger a re-render in your component. This can lead to stale data being displayed.

For example, imagine a component displaying a firstName and a lastName based on a prop. If the prop is destructured, the properties won't update when the parent component changes the prop value.

<template>
  <div>
    <p>Name: {{ firstname }} {{ lastname }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue';

// ❌ Non-reactive props - this won't re-render
const { firstName, lastName } = defineProps({
  firstName: String,
  lastName: String,
});
</script>
Enter fullscreen mode Exit fullscreen mode

Keeping destructured props reactive (if necessary)

If you still really, really want to destructure props, there are two ways to maintain reactivity:

  • Reference your props object directly using toRefs() Vue’s toRefs function takes a reactive object (like props) and returns a new object where each property is wrapped in a ref. Accessing properties using .value (e.g., prop1.value) ensures reactivity.

⚡ Vue.js documentation advises against this approach for props, suggesting using the dot notation instead. Read more

<template>
  <div>
    <p>Name: {{ firstName }} {{ lastName }}</p>
  </div>
</template>

<script setup>
import { ref, toRefs } from 'vue';

const props = defineProps({
  firstName: String,
  lastName: String,
})

const { firstName, lastName } = toRefs(props)
</script>
Enter fullscreen mode Exit fullscreen mode
  • Reference your props object directly using computed():
<template>
  <div>
    <p>{{ fullName }}</p>
  </div>
</template>

<script setup>
import { computed } from 'vue';
const { firstName, lastName } = defineProps({
  firstName: String,
  lastName: String
})
const fullName = computed(() => firstName + lastName)
</script>
Enter fullscreen mode Exit fullscreen mode

The Bottom Line: Embrace the Dot Notation!

While destructuring can be useful for non-reactive objects, it’s generally recommended to access props directly using props.propertyName in Vue.js.

<template>
  <div>
    <p>Name: {{ props.firstname }} {{ props.lastname }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue';

// ✅ Reactive props - this will re-render
const props = defineProps({
  firstName: String,
  lastName: String,
});
</script>
Enter fullscreen mode Exit fullscreen mode

This approach guarantees reactivity and avoids potential issues.


Remember, young traveler, maintaining reactivity is crucial for dynamic Vue components. Choose the approach that suits your style, but be mindful of the potential pitfalls of destructuring props.

Top comments (10)

Collapse
 
jbool24 profile image
Justin Bellero

If you don’t use props in the script setup tag you can just “defineProps” without assigning a local variable “props” and they are available in the template. Are you saying this is implied destructuring? Becuase I’ve been doing this and I still have reactivity. 🤷‍♂️ Maybe I am mistaken.

Example:

<template>
  <div>
    <p>Name: {{ firstname }} {{ lastname }}</p>
  </div>
</template>

<script setup lang=“ts”>
import { ref } from 'vue';

// this still will re-render
defineProps({
  firstName: String,
  lastName: String,
});
</script>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
helwatany profile image
Haithm EL-Watany • Edited

You will still have reactivity if you define the props without assigning the definition to a variable you will be able to use the props firstName and lastName in your example directly in the template and they will still be reactive.

The downside of this approach is that it only works if you are creating a simple component and all of the props will be used in the <template>, if you want to use the props in some logic like function or computed ... etc you have to assign the props definition to a variable

<template>
  <div>
    <p>Name: {{ firstname }} {{ lastname }}</p>
  </div>
</template>

<script setup lang=“ts”>
import { ref } from 'vue';

defineProps({
  firstName: String,
  lastName: String,
});

// ❌ Won't work
const fullName = computed(() => firstName+ lastName)
</script>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jbool24 profile image
Justin Bellero • Edited

Yes. That’s a good point! When I use the method I mentioned they are for base components and very simplex. I merely wanted to call out that it is possible to NOT assign props to a local variable to use in the template. If you need something more complex with props then your method along with computed values as above would be necessary.

<template>
  <div>
    <p>Name: {{ props.firstname }} {{ props.lastname }}</p>
    <p> {{ fullName }} </p>
  </div>
</template>

<script setup lang=“ts”>
import { ref } from 'vue';

const props = defineProps({
  firstName: String,
  lastName: String,
});

//  ✅ Is reactive when props change
const fullName = computed(() => props.firstName + props.lastName)
</script>
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
helwatany profile image
Haithm EL-Watany

For me I always go for assigning the props definition to a variable to set a standard for creating components with props in the project especially if there is a big team working on it.

Thread Thread
 
richardevcom profile image
richardev

Same here - much safer and less headache.

I actually wrote this article after reading a Tweet of a developer, who posted an example where he destructured props and runs into reactivity issues.

Thread Thread
 
jbool24 profile image
Justin Bellero • Edited

Not sure how that could cause headache 🤷 if you commit to only using that method in a BaseComponent type. You would NEVER use a based component directly as a rule; Only wrap a new component around that to encapsulate base functionality or style. It's a powerful technique for enterprise project that are VERY large and have many team members touching code. Once the base component is set according to the Design System spec you [in theory] never touch those classes directly. It's your core and all customization build on top where each team/dev that needs a specific "thing" wraps a base component to create what they need. Come time to Refactor only that teams component tree and views are risk factors for the change.

It's all preference. All methods are 'correct'. great post! 👍

Thread Thread
 
richardevcom profile image
richardev • Edited

I destructure variables in JS to improve performance, but at the very beginning (when I first started with Vue), I tried that with props and ran into the reactivity issues mentioned above. After some reading, I came to the conclusion that I should just go with dot notation. Also, I sometimes switch to computed functions with destructured props, just for overall readability, if I need an extra parse of the values. Long story short, I use two methods, depending on the use case.

Thank you - really appreciate your perspective on this.

Collapse
 
richardevcom profile image
richardev

Nope. That is not destructuring. Like @helwatany explains below - your example will still be reactive since you're not destructuring props.

Collapse
 
jbool24 profile image
Justin Bellero

@richardevcom Yes thank you. Than is my point exactly. You don't NEED to destructure IF you have a simple component, don't want to add extra memory allocation with a local 'prop' variable, and want to directly reference a prop value inside the tags. You can use the defineProps macro and just use the defined prop names in the template. They WILL be reactive.

Thread Thread
 
richardevcom profile image
richardev

True. Well, I prefer to use your example or dot notation and stay away from destructuring them (hence the article).