Destructuring Vue.js props: The Reactivity Challenge
richardevcom

richardevcom @richardevcom

About: Full-stack 💩- coder. Wannabe dev blogger. Aspiring tech-troll.

Location:
Latvia
Joined:
Aug 3, 2022

Destructuring Vue.js props: The Reactivity Challenge

Publish Date: Mar 25 '24
22 10

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.

Comments 10 total

  • Justin Bellero
    Justin BelleroApr 4, 2024

    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
    • Haithm EL-Watany
      Haithm EL-WatanyApr 5, 2024

      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
      • Justin Bellero
        Justin BelleroApr 5, 2024

        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
        • Haithm EL-Watany
          Haithm EL-WatanyApr 6, 2024

          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.

          • richardevcom
            richardevcomApr 6, 2024

            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.

            • Justin Bellero
              Justin BelleroApr 6, 2024

              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! 👍

              • richardevcom
                richardevcomApr 7, 2024

                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.

    • richardevcom
      richardevcomApr 6, 2024

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

      • Justin Bellero
        Justin BelleroApr 6, 2024

        @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.

        • richardevcom
          richardevcomApr 6, 2024

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

Add comment