Back when I started my internship at the company four months ago, I packaged a countdown component for a small application.

The link is here: the countdown component of wechat applets

Looking at the previous implementation from the current point of view is a pile of bad code that can’t be looked at. So I took the opportunity to refactor the previous components.

Refactoring old code

In the original component there was an initDuration property and three methods, countDown, format and runCountDown.

InitDuration properties

First we need three page properties to help with the rest of the code. They have the following names and contents:

timer: null.// Store the ID of setInterval
flag: false.// Indicates whether the countdown is over
num: 0 // The number of seconds passed
Copy the code

In the new callback method for the initDuration property, we encapsulate the clearTimer method, init initializes the method, and executes the countdown.

initDuration: {
  type: Number.value: 0.observer: function (newVal) {
    if (this.timer) {
      this.clearTimer()
    }
  
    this.init() // Reset num and flag
    this.runCountDown(newVal)
  }
},
Copy the code

It is important to note that observer callbacks are not triggered when the value of the passed property is the default value, such as 0 here.

/** * initializes the function */
init: function () {
  this.flag = false
  this.num = 0
}

/** * Clear the timer */
clearTimer: function () {
  clearInterval(this.timer)
  this.timer = null
}
Copy the code

CountDown method

The countDown method takes the number of seconds to countDown and returns a string of countDown seconds. There are no major changes in this method, just some code formatting changes. As follows:

/** * count the countdown * @param {Number} duration - time difference in seconds * @returns {string} count the string */
countDown: function (duration) {
  if (duration <= 0) {
    this.setFlag(true) // Set flag to true
    return '00:00:00' // Return to default time Settings
  }

  let seconds = this._format(duration % 60)
  let minutes = Math.floor(duration / 60)
  minutes = minutes >= 60 ? this._format(minutes % 60) : this._format(minutes)
  let hours = this._format(Math.floor(duration / 3600))

  return `${hours}:${minutes}:${seconds}`
},
Copy the code

The format method

The format method simply handles the presentation of numbers less than 10.

/** * Format a Number less than 10 * @param {Number} time - A Number less than 10 * @returns {string} Formatted string */
format: function (time) {
  return time >= 10 ? time : ` 0${time}`
},
Copy the code

RunCountDown method

The runCountDown method had a big change, and the original code was a mess of logic, with a lot of irrelevant code that should have been wrapped up to decouple.

runCountDown: function (initDuration) {
  SetData ({countDownStr});
  this.setCountDownTime(this.countDown(initDuration))

  // The countdown is updated every second
  this.timer = setInterval((a)= > {
    if (this.flag == true) { // The countdown is over
      clearInterval(this.timer)

      return undefined
    }

    this.addNum() // this.num += 1
    this.setCountDownTime(this._countDown(initDuration - this.num))
  }, 1000)},Copy the code

Add new features

Our original countdown component lacked some features, such as only passing in seconds, only displaying 00:00:00 when the countdown was over, and not initializing if the value passed in was 0 (this was a Bug). So we need to add the following new features:

  • Support custom countdown after the end of the reality string.
  • Fix a Bug where the value passed in is 0.
  • The time passed in can be either a number of seconds or a string of UTC time.

Custom end string

In the countdown component, the this.data.countDowntime property displays the countdown string. So at the end, set the value of the countDownTime property to the string you passed in. First, encapsulate an assignment method

setEndContent: function (countDownTime) {
  if (countDownTime) {
    this.setData({ countDownTime })
  }
}
Copy the code

Next, add a new attribute to the component called endContent.

endContent: {
  type: String.value: '00:00:00'
}
Copy the code

Next, at the end of the countdown, we call our assignment method, the timer callback of the runCountDown method.

this.timer = setInterval((a)= > {
  if (this.flag == true) {
    clearInterval(this.timer)
    
    this.setEndContent(this.properties.endContent) // Sets the end string
    
    return undefined
  }
    
  this.addNum()
  this.setCountDownTime(this._countDown(initDuration - this.num))
}, 1000)
Copy the code

This completes the custom string, passing in default values when using the component.

Fix a Bug where the value passed in is 0

The problem arises because the Observer callback is not called when the incoming property is the default value, so we need to use the component’s Attached life cycle function at this point.

attached: function () {
  if (this.properties.initDuration <= 0) {
    // If the observer callback is not called when the incoming value is zero, the countdown is shown directly from here
    this.setEndContent(this.properties.endContent)
  }
}
Copy the code

You can pass in a UTC time string

For the sake of brevity, we will not add new properties to the component and will still use the initDuration property, so change its type from Number to NULL (this is not strong enough for small programs to choose multiple types). . After changing type we need to encapsulate a method to parse the UTC time string into countdown seconds.

parseDate: function (date) {
  if (typeof date == 'string') {
    // Subtract the current time from the time passed in, and the result is the same as the value directly passed in
    return Math.floor((+new Date(date) / 1000)) - Math.floor((+new Date / 1000))}return date
}
Copy the code

Called in an Observer callback as follows:

initDuration: {
  type: null.observer: function (newVal) {
    if (this.timer) {
      this._clearTimer()
    }
  
    this._init()
    this._runCountDown(this.parseDate(newVal)) Call the parseData method here}}Copy the code

conclusion

During this refactoring, I saw that the previous code was so coupled that it just worked. If it’s expensive to add functionality to this, split the internal logic. Easy to read and understand, but also convenient to expand the function in the future. So after the refactoring we added two new features that we hope this article will help you with.

Countdown component code: github.com/MeloGuo/wxm… Copy the ‘count-down’ folder directly to the project directory for use. Welcome to like, follow, star, fork, and of course pr, issue.