Pinia stores not reactive in Vue3 components

89 Views Asked by At

I am using Pinia for stores in my Vue3 app with Typescript. I want to manage the current week that is being displayed. The problem arises when I try to access the weekDisplay variable from the store. It does not update its value when currentDate is modified, even though it is wrapped inside computed(). The changeDisplay() function is called from the PageHeader component, when certain buttons are pressed.

Note that currentDate is updated properly both inside the store and the component.

This is src/stores/Activities.ts:

import { defineStore } from "pinia";
import { computed, ref } from "vue";
import { weekName } from "@/main";
... /* Types, enums etc. */
export const useActivitiesStore = defineStore("activities", () => {
  const eventsMap = ref<Map<string, Activity[]>>(new Map());
  const currentDate = ref<Date>(new Date());
  const weekDisplay = computed(() => weekName(currentDate.value));
  function changeDisplay(change: number): void {
    currentDate.value.setDate(currentDate.value.getDate() + 7 * change);
  }
  return { eventsMap, currentDate, weekDisplay, changeDisplay };
});

This is src/components/PageHeader.vue:

<script setup lang="ts">
... /* Other imports */
import { useActivitiesStore } from "@/stores/Activities";
import { storeToRefs } from "pinia";
import { computed } from "vue";

let store = useActivitiesStore();

const { eventsMap, currentDate, weekDisplay } = storeToRefs(store);
</script>

<template>
  <header id="header">
    ...
    <section id="week-navigation-container" class="week-navigation">
      <article
        id="previous-week-button"
        class="week-button"
        @click="store.changeDisplay(-1)">
        <IconLeftArrow class="left-arrow" />
        <Tooltip text="Previous week" />
      </article>
      <article id="open-calendar-button" class="week-display">
        <h1 class="font-menu-title">
          {{ weekDisplay }}
        </h1>
        <Tooltip text="Open calendar" />
      </article>
      <article
        id="next-week-button"
        class="week-button"
        @click="store.changeDisplay(1)">
        <IconRightArrow class="right-arrow" />
        <Tooltip text="Next week" />
      </article>
    </section>
  </header>
</template>

This is src/main.ts:

import "./assets/main.css";

import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import router from "./router";

const pinia = createPinia();
const app = createApp(App);

app.use(router);
app.use(pinia);
app.mount("#app");

export function datesArr(date: Date) {
  ...
}

export function weekName(date: Date): string {
  ...
}

export function dayToTop(day: number) {
  ...
}

export function timeToLeft(hour: number, minute: number, second: number) {
  ...
}

export function timeToWidth(hour: number, minute: number) {
  ...
}

The text inside the h1 element with the .font-menu-title class should update when the buttons are pressed to display the relevant information. (The exact value would be the result of weekName(currentDate), which should always be stored in the weekDisplay variable).

1

There are 1 best solutions below

0
yoduh On

Updating the date with setDate is an inplace replacement which Vue reactivity can't detect. It's not ideal looking code but you can alter the line to include an assignment and guarantee the reactivity response is triggered:

function changeDisplay(change: number): void {
  currentDate.value = new Date(
    currentDate.value.setDate(currentDate.value.getDate() + 7 * change)
  )
}