vue.jsvitecircular-dependencypinia

How to avoid circular dependency in our Vue.js Project?


My project is created using Vue, Vite and Pinia. I used options api for communication in my project. I want to know the tips that I should follow to never face this problem so that I can always use it as a pattern in my code.

Song.vue:127  Uncaught ReferenceError: Cannot access 'useMusicStore' before initialization
    at Song.vue:127:17

this is my Song.vue componnent:

this is my template tag:

<template>
  <!-- Music Header -->
  <section class="w-full mb-8 py-14 text-center text-white relative">
    <div
        class="absolute inset-0 w-full h-full box-border bg-contain music-bg"
        style="background-image: url(/assets/img/song-header.png)"
    ></div>
    <div class="container mx-auto flex items-center">
      <!-- Play/Pause Button -->
      <button @click.prevent="playMusic(music)"
          type="button"
          class="z-50 h-24 w-24 text-3xl bg-white text-black rounded-full focus:outline-none"
      >
        <i class="fas" :class="{'fa-play': !playing, 'fa-pause': playing}"></i>
      </button>
      <div class="z-50 text-left ml-8">
        <!-- Song Info -->
        <div class="text-3xl font-bold">{{music.displayName}}</div>
        <div>{{music.gener}}</div>
      </div>
    </div>
  </section>
  <!-- Form -->
  <section class="container mx-auto mt-6">
    <div
        class="bg-white rounded border border-gray-200 relative flex flex-col"
    >
      <div class="px-6 pt-6 pb-5 font-bold border-b border-gray-200">
        <!-- Comment Count -->
        <span class="card-title">Comments ({{music.commentCount}})</span>
        <i class="fa fa-comments float-right text-green-400 text-2xl"></i>
      </div>
      <div class="p-6" v-show="userLoggedIn">
        <!--Comment Message Box-->
        <div class="text-white text-center font-bold p-4 rounded mb-4"
             v-if="comment_show_alert" :class="comment_alert_variant">
          {{ comment_alert_msg}}
        </div>
        <!--Comment Form-->
        <vee-form @submit="postComment">
            <vee-field
                type="textarea"
                name="comment"
                class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded mb-4"
                placeholder="Your comment here...">
            </vee-field>
          <error-message name="comment" class="text-red-600"></error-message>
          <button
              :disabled="comment_in_submission"
              type="submit"
              class="py-1.5 px-3 rounded text-white bg-green-600 block"
          >
            Submit
          </button>
        </vee-form>
        <!-- Sort Comments -->
        <select v-model="sort"
            class="block mt-4 py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
        >
          <option value="cratedAt" selected>Latest</option>
          <option value="-cratedAt">Oldest</option>
        </select>
      </div>
    </div>
  </section>
  <!-- Comments -->
  <ul class="container mx-auto">
    <CommentItem v-for="comment in comments"
    :key="comment._id"
    :comment="comment"
    />

  </ul>

</template>

and this is my script tag:

<script>
import useMusicStore from '@/stores/music.js'
import useCommentStore from '@/stores/comment.js'
import useUserStore from '@/stores/user.js'
import usePlayerStore from '@/stores/player.js'

import CommentItem from './base/CommentItem.vue'

import {mapState, mapActions, mapWritableState} from "pinia";
import { ErrorMessage, Field, Form } from 'vee-validate'


export default {
  data(){
    return {
      commentSchema:{
        comment: 'required|min:8|max:10',
      },
      sort:'cratedAt'
    }
  },
  name: 'Song',
  components: {
    ErrorMessage,
    Field,
    Form,
    CommentItem
  },
  created(){
    this.musicStore = useMusicStore(); // initialize musicStore in created lifecycle hook
    this.fetchMusic(this.$route.params.songId)
    this.fetchComments(this.$route.params.songId, this.sort)
  },
  computed:{
    musicStore(){
      return useMusicStore()
    }
    ,
    music(){
      return this.musicStore.music
    },
    ...mapWritableState(useCommentStore, [
        'comments',
        'commentBoxMsg',
        'comment_show_alert',
        'comment_alert_variant',
        'comment_alert_msg',
        'comment_in_submission'
    ]),
    ...mapState(useUserStore, ['userLoggedIn']),
    ...mapState(useMusicStore, ['playing'])
  },
  methods:{
    ...mapActions(useCommentStore, {getCommentsByMusicId: "getCommentsByMusicId"}),
    ...mapActions(useCommentStore, {createComment: "createComment"}),
    ...mapActions(usePlayerStore, {playMusic: "playMusic"}),

    async fetchMusic(songId){
      try {
        await this.musicStore.getOneMusic(songId)
      }catch(error){
        console.log('Error fetching music', error)
      }
    },
    async fetchComments(songId, sortBy){
      try {
        await this.getCommentsByMusicId(songId, sortBy)
      }catch(error){
        console.log('Error fetching comments', error)
      }
    },
    async postComment(values){
      this.comment_in_submission = true
      try{
        console.log(values)
        let reqBody = {
          music: this.music._id
        }
        reqBody.comment = values.comment
        console.log(reqBody)
        await this.createComment(reqBody)
      }catch(error){
        console.log('Error creating comment', error)
      }finally {
        this.comment_in_submission=false
        this.comment_show_alert = false
      }
    }
  },
  watch:{
    sort(){
      this.fetchComments(this.$route.params.songId, this.sort)
    }
  }
}
</script>

Solution

  • I fixed this problem. I define a computed property instead of using mapState().

        computed:{
        musicStore(){
          return useMusicStore()
        }
        ,
        music(){
          return this.musicStore.music
        },
        ...mapWritableState(useCommentStore, [
            'comments',
            'commentBoxMsg',
            'comment_show_alert',
            'comment_alert_variant',
            'comment_alert_msg',
            'comment_in_submission'
        ]),
        // ...mapState(useMusicStore, ['playing'])
        playing(){
          return this.musicStore.playing
        },
        ...mapState(useUserStore, ['userLoggedIn']),
      }