Search code examples
javascriptvue.jsvuejs3vue-component

Vue3 sub-component not updating when parent component data changes


I am having trouble with Vue 3. When I use a subcomponent and the data that is being used to generate the content is updated, the subcomponent does not reactively update. I am using the setup() function for the subcomponent, and my understanding was that props in Vue 3 are reactive, so I am at a loss for where I am going wrong.

This is my parent component:

<q-markup-table :v-show="!isLoading">
  <thead>
    <tr>
      <th>Name</th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="(detailedPerson, index) in people" :key="index">
      <td>
        <PersonLink :person="buildPerson(detailedPerson)"></PersonLink>
      </td>
    </tr>
  </tbody>
</q-markup-table>

With the following method:

  data() {
    return {
      people: {} as DetailedPerson[],
    };
  },
  methods: {
    buildPerson(detailedPerson: DetailedPerson): Person {
      return {
        slug: person.slug,
        firstName: person.firstName,
        lastName: person.lastName,
      };
    },
  }

When the people list is updated, the PersonLink does not update; it is acting as though the props are not reactive. This is the PersonLink code:

<template>
  <router-link :to="url">{{ name }}</router-link>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { NameFormatter } from 'src/common/formatters';
import { Person } from './person.models';

export default defineComponent({
  props: {
    person: {
      required: true,
      type: Object as () => Person,
    },
  },
  setup(props) {
    return {
      url: `/person/${props.person.slug}`,
      name: NameFormatter.formatInverseName(props.person),
    };
  },
});
</script>

What needs to be done to ensure reactivity when the subcomponent props are updated at the parent component level?


Solution

  • Props are reactive, but you construct static values from them, so those values are not updated.

    You must use a computed ref to create the url and name, like so:

    import { defineComponent, computed } from 'vue';
    
    setup(props) {
      const url = computed(() => `/person/${props.person.slug}`);
      const name = computed(() => NameFormatter.formatInverseName(props.person));
    
      return {
        url,
        name,
      };
    }
    

    Now url and name will update when their dependencies change, and so will anything that depends on them.