ポモドーロタイマーがページ遷移やリロード後に初期化されてしまう原因は、状態(残り時間やタイマーの進行状況など)をコンポーネントのローカル状態にのみ保持しているためです。これを防ぐためには、状態を外部ストア(例:Pinia)に保持し、必要に応じてローカルストレージと同期することでリロード耐性を持たせられます。
以下に、Piniaを使って状態管理を導入し、リロード後にもタイマーが継続するように修正したコードと解説を記載します。
✅ 修正手順まとめ
- Piniaのストアを作成して、タイマー状態を一元管理。
PomodoroTimer.vue
からストアを利用して、状態をローカルではなくストアに保存。- 状態を 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 ストアで状態を管理 | isRunning や remainingSeconds を全コンポーネントで共有可能に |
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");
この実装で「カレンダーから戻ったら初期化される」問題は解消され、リロード後にもタイマーが継続されるようになります。