Search code examples
vue.jsvue-reactivitypinia

Why my vue components don't update when the state in pinia changes?


I'm working on a website that uses vue and firebase. After the authentication part the user gets to the dashboard where there is the list of his projects that are a subcollection of the user document in firestore.

I made a pinia store that manages this data and each time a project is created with the form or gets deleted the state.projects updates with the new array of projects that gets cycled to display the list in the view.

Inside the view I have access to the store.projects thanks to a getter that should be reactive but when I add or delete a project nothing happens in the view, but still the state.projects gets updated.

here is the code of the DashboardView.vue:

 <template>
  <MainHeader mode="dashboard" />

  <main class="main">
    <div class="main__container">
      <section class="main__section">
        <div class="section__header">
          <h1 class="header__title">Projects</h1>
          <!-- <TextInput type="text" placeholder="Search" v-model="filter" /> -->
        </div>
        <div class="section__content">
          <ul class="content__list">
            <li
              v-for="project in projects"
              :key="project.id"
              class="content__item"
            >
              {{ project.id }}
              <!-- <router-link
                :to="{ name: 'ProjectView', params: { id: project.id} }">
              </router-link> -->
              <SimpleButton @click="deleteProject(project.id)" type="button" text="delete" />
            </li>
          </ul>
        </div>
        <div class="section__footer">
          <form @submit.prevent="createProject">
            <TextInput type="text" placeholder="name" v-model="form.id" />
            <TextInput type="text" placeholder="website" v-model="form.website" />
            <SimpleButton type="submit" text="Add" />
          </form>
        </div>
      </section>
    </div>
  </main>
</template>

<script>
import { useUserDataStore } from "../stores/UserDataStore.js";
import MainHeader from "../components/MainHeader.vue";
import SimpleButton from "../components/SimpleButton.vue";
import TextInput from "../components/TextInput.vue";
import { ref } from '@vue/reactivity';

export default {
  name: "DashboardView",
  components: {
    MainHeader,
    SimpleButton,
    TextInput,
  },
  setup() {

    // const filter = "";
    const form = ref({});
    const userDataStore = useUserDataStore();
    const projects  = userDataStore.getProjects;

    const createProject = () => {
      userDataStore.createProject(form.value)
    }

    const deleteProject = (id) => {
      userDataStore.deleteProject(id)
    }

    return {
      projects,
      form,
      createProject,
      deleteProject,
    };

  },
};
</script>

And here the pinia store code:

   import { defineStore } from "pinia";
import router from "../router";
import { db } from '../firebase';
import { doc, setDoc, getDoc, getDocs, collection, deleteDoc } from 'firebase/firestore'

export const useUserDataStore = defineStore('UserDataStore', {
    state: () => {
        userData: { }
        projects: []
        uid: null
    },
    actions: {
        createNewUser(uid, name) {
            setDoc(doc(db, "users", uid), {
                name
            })
                .then(() => {
                    this.fetchUserData(uid)
                })
                .catch((error) => console.log(error))
        },

        fetchUserData(uid) {
            this.uid = uid

            // Fetch user doc with uid
            getDoc(doc(db, "users", uid))
                .then((response) => {
                    this.userData = response.data()

                    // Fetch user projects
                    getDocs(collection(db, "users", uid, "projects"))
                        .then((response) => {

                            const projectsArray = []

                            response.forEach(el => {
                                projectsArray.push({ data: el.data(), id: el.id})
                            })

                            this.projects = projectsArray
                            console.log(this.projects);

                            router.push({ name: 'DashboardView' })
                        })
                })
                .catch((error) => console.log(error))
        },

        createProject(details) {

            const { id, website } = details

            setDoc(doc(db, "users", this.uid, "projects", id), {
                website
            }).then(() => {
                console.log('created');
                this.fetchUserData(this.uid)
            })
            .catch((err) => console.log(err))
        },

        deleteProject(id) {
            deleteDoc(doc(db, "users", this.uid, "projects", id))
                .then(() => {
                    console.log('deleted');
                    this.fetchUserData(this.uid);
                })
                .catch(err => console.log(err))
        }
    },

    getters: {
        getProjects: (state) => state.projects
    }
})

Solution

  • A store is reactive object, the reactivity of store property is disabled at the time when it's accessed in setup function:

    const projects  = userDataStore.getProjects;
    

    It should be either:

    const projects  = computed(() => userDataStore.getProjects);
    

    Or:

    const { getProjects: projects } = storeToRefs(userDataStore);