Search code examples
javascriptvue.jsvuex

Order of Props, Computed, and Created in vuejs/vuex


I have some question about the right order to manipulate props, computed and created with vue and vuex.

I have the following code

<script>
  import { mapGetters } from 'vuex'

  export default {
    data () {
      return {}
    },
    props: ['id'],
    methods: {
    },
    computed: {
      ...mapGetters({
        semestre: 'semestre/show/item'
      }),
      titre: function () {
        return this.semestre.nom
      }
    },
    created () {
      this.$store.dispatch('semestre/show/retrieve', parseInt(this.id))
    }
  }
</script>

But I have an error, on the computed titre which manipulate an undefined variable "semestre".

It seems that computed is executed before created. So If i try to use beforeCreate in the place of Created, it doesn't work, because props does not exist.

So I think

beforeCreate => Props => Computed => Created ?

But how can I execute correctly my code ? I need to get a value in props, pass this value to vuex, and then manipulate the result of VueX. Maybe, I don't understand something in the logic of vue/vueX.


Solution

  • If your computed is used in the view, then yes it will be calculated before semestre is provided since it appears that your vuex action is asynchronous. If it is asynchronous, your created function will finish executing prior the action is completed. Due to this, the getter 'semestre/show/item' will always return an undefined value until the 'semestre/show/retrieve' action is fully complete, which is not going to be before created is finished executing.

    To handle this, you have to write your components so that they handle asynchronous loading of values. You basically have to write your component so that it handles the case where it has not been loaded yet, and handle the case where it has loaded. I suggest you change your computed so that it handles undefined or null values:

    titre: function () {
        return this.semestre && this.semestre.nom;
        //or if you want to provide a default value
        return (this.semestre && this.semestre.nom) || '??';
    }
    

    A more robust solution is one where you provide feedback to your user that values are loading by tracking whether your action has completed or not.

    <template>
        <div v-if="isLoading">Content is load (you could display a spinner)</div>
        <div v-else>
            Display your semestre content here:
            {{titre}}
        </div>
    </template>
    <script>
      import { mapGetters } from 'vuex'
    
      export default {
        data () {
          return {
              isLoading: false,
          };
        },
        props: ['id'],
        computed: {
          ...mapGetters({
            semestre: 'semestre/show/item'
          }),
          titre: function () {
            return this.semestre.nom
          }
        },
        async created () {
          this.isLoading = true;
          await this.$store.dispatch('semestre/show/retrieve', parseInt(this.id))
          this.isLoading = false;
        }
      }
    </script>
    

    And if you are not able to use async/await keywords, you can do the following:

    created () {
        this.isLoading = true;
        this.$store.dispatch('semestre/show/retrieve', parseInt(this.id))
            .then(() => {
                this.isLoading = false;
            });
    }