Estoy intentando configurar un evento de botón en Vue 3 que muestra una cuenta regresiva setTimeout en el botón antes de enrutar a otra página. Mi función de evento incluye un condicional que ejecuta una cuenta regresiva de 5 a 0 cuando countValue es > 0. La función setTimeout es recursiva en el sentido de que llama a la función goHome() cada vez que ejecuta una cuenta regresiva. Cuando countValue es 0, se llama a router.push para redirigir a otra página. Mi problema es que cuando presiono el botón, la cuenta regresiva se agrupa inmediatamente de 5 a 4. Quiero retrasar brevemente la cuenta regresiva cuando presiono el botón por primera vez. En otras palabras, no quiero que la cuenta regresiva salte inmediatamente a 4 cuando presione el botón. ¿Cómo puedo arreglar esto?
Aquí está mi código hasta ahora:
Modelo:
<div> <button @click="goHome" type="button" :disabled="disabledOnCount === true ? true : false">Go Home in {{ countValue }} seconds</button> </div>Guion:
import { ref } from '@vue/reactivity' const countValue = ref(5) const goHome = () => { if (countValue.value > 0) { disabledOnCount.value = true countValue.value -= 1 setTimeout(() => { goHome() if (countValue.value === 0) { router.push({ name: 'home' }) } }, 1000) } }La cuenta regresiva salta a 4 inmediatamente porque goHome() inmediatamente disminuye el valor y luego llama a setTimeout() para verificar el valor. Cambiar el valor de la cuenta regresiva y leerlo debería ocurrir en el mismo tick:
const goHome = () => { if (countValue.value > 0) { disabledOnCount.value = true countValue.value -= 1 // ❌ decremented immediately setTimeout(() => { goHome() if (countValue.value === 0) { router.push({ name: 'home' }) } }, 1000) } }Algunos cambios:
goHome() siempre debe llamar a setTimeout() para que su efecto siempre se retrase.
La devolución de llamada del temporizador solo debe llamar a goHome() cuando la cuenta regresiva es distinta de cero. De lo contrario, la devolución de llamada no tiene efecto, por lo que no es necesario llamarla.
const goHome = () => { disabledOnCount.value = true 1️⃣ setTimeout(() => { if (countValue.value > 0) { countValue.value -= 1 if (countValue.value === 0) { router.push({ name: 'home' }) } else { 2️⃣ goHome() } } }, 1000) }Si es posible que la página se desmonte antes de que finalice el temporizador (por ejemplo, cuando el usuario cambia la página), el temporizador se debe detener en el desmontaje para liberar recursos. De lo contrario, el temporizador puede continuar ejecutándose mientras el usuario se ha alejado y luego, de repente, se lo empuja a la página de inicio inesperadamente.
Para detener el temporizador, debe rastrear la ID del temporizador y luego usar el gancho onUnmounted para borrar el tiempo de clearTimeout() en esa ID:
import { onUnmounted } from 'vue' let countdownTimerId = null const goHome = () => { countdownTimerId = setTimeout(() => { /* same as above */ if (countValue.value === 0) { countdownTimerId = null } }, 1000) } onUnmounted(() => clearTimeout(countdownTimerId))