DEV Community

Cover image for Countdown component in react native
Ajmal Hasan
Ajmal Hasan

Posted on • Updated on

Countdown component in react native

Countdown component that can be updated and also used in lists/flatlist.

Code:

import React from 'react';
import PropTypes from 'prop-types';
import { View, Text, AppState } from 'react-native';
import { translate } from '../../../helpers/Localization';

class CountDown extends React.Component {
  static propTypes = {
    id: PropTypes.string,
    until: PropTypes.number,
    onChange: PropTypes.func,
    onFinish: PropTypes.func,
  };

  state = {
    until: Math.max(this.props.until, 0),
    lastUntil: null,
    wentBackgroundAt: null,
  };

  constructor(props) {
    super(props);
    this.timer = setInterval(this.updateTimer, 1000);
  }

  componentDidMount() {
    this.appStateSubscription = AppState.addEventListener(
      'change',
      this._handleAppStateChange,
    );
  }

  componentWillUnmount() {
    clearInterval(this.timer);
    this.appStateSubscription.remove();
  }

  componentDidUpdate(prevProps, prevState) {
    if (
      this.props.until !== prevProps.until ||
      this.props.id !== prevProps.id
    ) {
      this.setState({
        lastUntil: 0,
        until: Math.max(this.props.until, 0),
      });
    }
  }

  _handleAppStateChange = currentAppState => {
    const { until, wentBackgroundAt } = this.state;
    if (
      currentAppState === 'active' &&
      wentBackgroundAt &&
      this.props.running
    ) {
      const diff = (Date.now() - wentBackgroundAt) / 1000.0;
      this.setState({
        lastUntil: until,
        until: Math.max(0, until - diff),
      });
    }
    if (currentAppState === 'background') {
      this.setState({ wentBackgroundAt: Date.now() });
    }
  };

  secondsToDhms = () => {
    const { until } = this.state;
    let seconds = Number(until);
    var d = Math.floor(seconds / (3600 * 24));
    var h = Math.floor((seconds % (3600 * 24)) / 3600);
    var m = Math.floor((seconds % 3600) / 60);
    var s = Math.floor(seconds % 60);

    var dDisplay = d > 0 ? d + 'd  ' : '';
    var hDisplay = h > 0 ? h + 'h  ' : d > 0 ? '12h  ' : '';
    var mDisplay = m > 0 ? m + 'm  ' : h > 0 ? '60m  ' : '';
    var sDisplay = s > 0 ? s + 's ' : m > 0 ? '60s' : '';
    return dDisplay + hDisplay + mDisplay + sDisplay;
  };

  updateTimer = () => {
    if (this.state.lastUntil === this.state.until || !this.props.running) {
      return;
    }
    if (
      this.state.until === 1 ||
      (this.state.until === 0 && this.state.lastUntil !== 1)
    ) {
      if (this.props.onFinish) {
        this.props.onFinish();
      }
      if (this.props.onChange) {
        this.props.onChange(this.state.until);
      }
    }

    if (this.state.until === 0) {
      this.setState({ lastUntil: 0, until: 0 });
    } else {
      if (this.props.onChange) {
        this.props.onChange(this.state.until);
      }
      this.setState({
        lastUntil: this.state.until,
        until: Math.max(0, this.state.until - 1),
      });
    }
  };

  renderCountDown = () => {
    return (
      <Text style={this.props.textStyle}>
        {this.secondsToDhms() || translate('timeOver')}
      </Text>
    );
  };

  render() {
    return <View style={this.props.style}>{this.renderCountDown()}</View>;
  }
}

CountDown.defaultProps = {
  until: 0,
  size: 15,
  running: true,
};

export default CountDown;


Enter fullscreen mode Exit fullscreen mode

USAGE

          <CountdownComponent
            until={moment(item?.time_left).diff(
              moment(item?.serverTimeForSocket),
              'seconds',
            )}
            textStyle={styles.timerTextAHL}
          />
Enter fullscreen mode Exit fullscreen mode

Discussion (0)