I am trying to build a timer using @pinia/nuxt with nuxt 3. I also want to make an http request when the timer is started to sync my db.
The problem I am facing is that whenever I call the action start
, the http request is made for each iteration of setInterval
and I only want to run it once.
This is the pinia module. I am calling the start
action from an onClick
event in a component.
state: () => ({
timer: {
id: null,
isRunning: false,
time: 5,
timer: 0,
state: null,
} as Timer,
}),
actions: {
start() {
this.timer.isRunning = true
this.syncTimer()
if (!this.timer.timer) {
this.timer.timer = setInterval(() => {
if (this.timer.time > 0) {
this.timer.time--
} else {
clearInterval(this.timer.timer)
this.reset()
}
}, 1000)
}
},
stop() {
this.timer.isRunning = false
clearInterval(this.timer.timer)
this.timer.timer = 0
},
reset() {
this.stop()
this.timer.time = 1500
},
syncTimer() {
backendAPI<Timer>('/api/timer', {
method: 'POST',
body: this.timer
}).then(({ error, data }) => {
if (!error.value) {
const id = data.value?.id ?? ""
this.timer.id = id
this.timer.state = "created"
}
})
}
}
Custom fetch composable backendAPI.ts
:
import type { UseFetchOptions } from 'nuxt/app'
import { useUserStore } from '@/stores/user'
import { defu } from 'defu'
export function backendAPI<T>(url: string, options: UseFetchOptions<T> = {}) {
const config = useRuntimeConfig()
const { token } = useUserStore();
const appToken = useRuntimeConfig().public.appToken
const defaults: UseFetchOptions<T> = {
baseURL: config.public.baseURL ?? 'http://localhost:4000',
key: url,
params: {
token: appToken
},
headers: token
? { Authorization: `Bearer ${token}` }
: {},
}
const params = defu(options, defaults)
return useFetch(url, params)
}
The problem seems to be related to the nuxt 3 useFetch
function.
From the nuxt documentation:
All fetch options can be given a computed or ref value. These will be watched and new requests made automatically with any new values if they are updated.
So every time the timer value is updated, useFecth
will automatically make a new request.
Adding watch: false
to the request solved my problem
backendAPI<Timer>('/api/timer', {
method: 'POST',
watch: false,
body: this.timer
}).then(({ error, data }) => {
if (!error.value) {
const id = data.value?.id ?? ""
this.timer.id = id
this.timer.state = "created"
}
})