Uncategorized

ページを移動するとタイマーがリセットされてしまう

ポモドーロタイマーがページ遷移やリロード後に初期化されてしまう原因は、状態(残り時間やタイマーの進行状況など)をコンポーネントのローカル状態にのみ保持しているためです。これを防ぐためには、状態を外部ストア(例:Pinia)に保持し、必要に応じてローカルストレージと同期することでリロード耐性を持たせられます。

以下に、Piniaを使って状態管理を導入し、リロード後にもタイマーが継続するように修正したコードと解説を記載します。


✅ 修正手順まとめ

  1. Piniaのストアを作成して、タイマー状態を一元管理。
  2. PomodoroTimer.vue からストアを利用して、状態をローカルではなくストアに保存。
  3. 状態を localStorage に保存・復元して、リロード対策。

📦 1. stores/usePomodoroStore.js を作成

jsコピーする編集する// stores/usePomodoroStore.js
import { defineStore } from "pinia";

export const usePomodoroStore = defineStore("pomodoro", {
  state: () => ({
    isRunning: false,
    isBreakTime: false,
    remainingSeconds: 1500, // 初期25分
    workDuration: 25,
    breakDuration: 5,
    lastTimestamp: null,
  }),
  actions: {
    start(duration) {
      this.isRunning = true;
      this.remainingSeconds = duration;
      this.lastTimestamp = Date.now();
      this.saveToStorage();
    },
    tick() {
      const now = Date.now();
      const elapsed = Math.floor((now - this.lastTimestamp) / 1000);
      this.remainingSeconds -= elapsed;
      this.lastTimestamp = now;
      this.saveToStorage();
    },
    reset() {
      this.isRunning = false;
      this.isBreakTime = false;
      this.remainingSeconds = this.workDuration * 60;
      this.lastTimestamp = null;
      this.saveToStorage();
    },
    saveToStorage() {
      localStorage.setItem("pomodoroState", JSON.stringify(this.$state));
    },
    loadFromStorage() {
      const saved = localStorage.getItem("pomodoroState");
      if (saved) {
        Object.assign(this, JSON.parse(saved));
      }
    },
  },
});

🧭 2. PomodoroTimer.vue の修正

vueコピーする編集する<script setup>
import { computed, onMounted, onUnmounted } from "vue";
import { usePomodoroStore } from "../stores/usePomodoroStore";

const store = usePomodoroStore();

onMounted(() => {
  store.loadFromStorage();
  if (store.isRunning) {
    timerInterval = setInterval(() => {
      store.tick();
    }, 1000);
  }
});

onUnmounted(() => {
  clearInterval(timerInterval);
  store.saveToStorage();
});

let timerInterval = null;

const formattedTime = computed(() => {
  const minutes = String(Math.floor(store.remainingSeconds / 60)).padStart(2, "0");
  const seconds = String(store.remainingSeconds % 60).padStart(2, "0");
  return `${minutes}:${seconds}`;
});

function startTimer() {
  if (store.isRunning) return;
  const duration = (store.isBreakTime ? store.breakDuration : store.workDuration) * 60;
  store.start(duration);
  timerInterval = setInterval(() => {
    store.tick();
  }, 1000);
}

function resetTimer() {
  clearInterval(timerInterval);
  store.reset();
}
</script>

<template>
  <div class="pomodoro-timer">
    <h2>{{ store.isBreakTime ? "休憩タイマー" : "ポモドーロ作業タイマー" }}</h2>
    <p>{{ formattedTime }}</p>
    <button @click="startTimer" :disabled="store.isRunning">開始</button>
    <button @click="resetTimer">リセット</button>
    <div>
      <label>作業時間(分):</label>
      <input type="number" v-model="store.workDuration" :disabled="store.isRunning" />
    </div>
    <div>
      <label>休憩時間(分):</label>
      <input type="number" v-model="store.breakDuration" :disabled="store.isRunning" />
    </div>
  </div>
</template>

🛠 修正点の解説

修正内容解説
Pinia ストアで状態を管理isRunningremainingSeconds を全コンポーネントで共有可能に
localStorage と同期リロード後もタイマーが継続されるように復元
タイマー更新 (tick) の制御setInterval で経過時間を反映し、残り時間を更新

✅ 最後に

  • main.js または main.ts にて Pinia を登録してください:
jsコピーする編集するimport { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";

const app = createApp(App);
app.use(createPinia());
app.mount("#app");

この実装で「カレンダーから戻ったら初期化される」問題は解消され、リロード後にもタイマーが継続されるようになります。

-Uncategorized