vuejs2vue-composition-apivue-apollo

Refresh my Vue Apollo query when params change


I am new to vue, but I don't understand why no one seems to be able to answer this question. I would have thought it was simple, but I can't find anywhere talking about it. I have this method getCategory that uses Apollo to query our graphQL API. The logic looks like this:

    import { useQuery, useResult } from "@vue/apollo-composable";
    import * as getCategoryBySlug from "@graphql/api/query.category.gql";

    export function useGetCategory(slug: string) {
      const { result, loading, error } = useQuery(getCategoryBySlug, { slug });
      const category = useResult(result, null, (data) => data.categoryBySlug);
      return { category, loading, error };
    }

When I want to use this in a component, I can simply do this:

    import { computed, defineComponent } from "@vue/composition-api";

    import { useGetCategory } from "@logic/get-category";
    import CategoryTitle from "@components/category-title/category-title.component.vue";
    import Products from "@components/products/products.component.vue";

    import { defineComponent } from "@vue/composition-api";

    import { useGetCategory } from "@logic/get-category";
    import CategoryTitle from "@components/category-title/category-title.component.vue";
    import Products from "@components/products/products.component.vue";

    export default defineComponent({
      name: "Categories",
      components: { CategoryTitle, Products },
      setup(_, context) {
        const { category, loading, error } = useGetCategory(
          context.root.$route.params.slug
        );

        return { category, loading, error };
      },
    });

And that's fine. Then in my template, I can do what I need to do like this:

    <template>
      <div>
        <category-title v-if="category" :category="category"> </category-title>
        <base-loader :loading="loading"> </base-loader>
        <products :category="category" v-if="category"></products>
      </div>
    </template>

    <script src="./category.component.ts" lang="ts"></script>
    <style src="./category.component.scss" lang="scss" scoped></style>

Now comes the issue (which in my mind, should be dead easy). I need to handle route changes, specifically the slug. So I have changed my code to this:

    import { computed, defineComponent } from "@vue/composition-api";

    import { useGetCategory } from "@logic/get-category";
    import CategoryTitle from "@components/category-title/category-title.component.vue";
    import Products from "@components/products/products.component.vue";

    export default defineComponent({
      name: "Categories",
      components: { CategoryTitle, Products },
      setup(_, context) {
        const result = computed(() => {
          return useGetCategory(context.root.$route.params.slug);
        });

        return { result };
      },
    });

which means I have to update my template to this:

    <template>
      <div v-if="result">
        <category-title
          v-if="result.category.value"
          :category="result.category.value"
        >
        </category-title>
        <base-loader :loading="result.loading.value"> </base-loader>
        <products
          :category="result.category.value"
          v-if="result.category.value"
        ></products>
      </div>
    </template>

    <script src="./category.component.ts" lang="ts"></script>
    <style src="./category.component.scss" lang="scss" scoped></style>

Which is just ugly. My question is this, can I destructure the computed property or something so my template can stay the same as it was?


Solution

  • You can destructure object returned by your computed but you will lose reactivity (category, loading, error variables created by destructuring computed value will not be updated when computed re-evaluates)

    What you want is to use Vue Apollo ability to refresh query when it's variables change

        import { useQuery, useResult } from "@vue/apollo-composable";
        import * as getCategoryBySlug from "@graphql/api/query.category.gql";
    
        export function useGetCategory(params) {
          const { result, loading, error } = useQuery(getCategoryBySlug, params);
          const category = useResult(result, null, (data) => data.categoryBySlug);
          return { category, loading, error };
        }
    

    in component...

        import { computed, defineComponent } from "@vue/composition-api";
    
        import { useGetCategory } from "@logic/get-category";
        import CategoryTitle from "@components/category-title/category-title.component.vue";
        import Products from "@components/products/products.component.vue";
    
        export default defineComponent({
          name: "Categories",
          components: { CategoryTitle, Products },
          setup(_, context) {
            const params = computed(() => 
              return {
                 slug: context.root.$route.params.slug
              }
            ) 
           
            const { category, loading, error } = useGetCategory(params);
          },
        });