Home > other >  How to delay initial button event in Vue 3 and JavaScript
How to delay initial button event in Vue 3 and JavaScript

Time:08-16

I am attempting to set up a button event in Vue 3 that displays a setTimeout countdown on the button before routing to another page. My event function includes a conditional that executes a countdown from 5 to 0 when the countValue is > 0. The setTimeout function is recursive in that it calls the goHome() function each time to execute a countdown. When the countValue is 0, then router.push is called to re-route to another page. My issue is that when I press the button, the countdown immediately lumps from 5 to 4. I am wanting to briefly delay the countdown when I first push the button. In other words, I do not want the countdown to immediately jump to 4 when I push the button. How can I fix this?

Here is my code so far:

Template:

<div>
  <button @click="goHome" type="button" :disabled="disabledOnCount === true ? true : false">Go Home in {{ countValue }} seconds</button>
</div>

Script:

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)
  }
}

CodePudding user response:

The countdown jumps to 4 immediately because goHome() immediately decrements the value, and then calls setTimeout() to check the value. Changing the countdown value and reading it should happen in the same 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)
  }
}

Solution

A few changes:

  1. goHome() should always call setTimeout() so that its effect is always delayed.

  2. The timer callback should only call goHome() when the countdown is nonzero. Otherwise, the callback has no effect, so no need to call it.

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)
}

If it's possible for the page to be unmounted before the timer finishes (e.g., when the user changes the page), the timer should be stopped on unmount to free resources. Otherwise, the timer can continue running while the user has navigated away, and then suddenly, they're pushed to the home page unexpectedly.

To stop the timer, you have to track the timer ID, and then use the onUnmounted hook to clearTimeout() on that 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))

demo

  • Related