Skip to content

Instantly share code, notes, and snippets.

@johndavedecano
Last active May 25, 2022 13:36
Show Gist options
  • Save johndavedecano/0630b27d2c0a9d717ae6fe918edcf534 to your computer and use it in GitHub Desktop.
Save johndavedecano/0630b27d2c0a9d717ae6fe918edcf534 to your computer and use it in GitHub Desktop.
TimeAgo component using date-fns
import React, {Component} from 'react';
import differenceInSeconds from 'date-fns/difference_in_seconds';
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
class TimeAgo extends Component {
componentDidMount() {
if (this.props.isLive) {
this.updateTime();
}
}
updateTime = () => {
const interval = this.getInterval();
if (interval > 0) {
setTimeout(this.updateTime, interval);
this.forceUpdate();
}
}
getDifference() {
return differenceInSeconds(new Date(), this.props.date);
}
getInterval() {
const diff = this.getDifference();
if (diff < 3600) {
return 60000;
} else if (diff >= 3600 && diff <= 86400) {
return 3600000;
} else {
return 0;
}
}
getParsedDate() {
const diff = this.getDifference();
if (diff < 30) {
return 'now';
} else {
const options = {
addSuffix: this.props.addSuffix,
includeSeconds: this.props.includeSeconds,
};
return distanceInWordsToNow(this.props.date, options);
}
}
render() {
return React.createElement(
this.props.element,
{className: (this.props.className) ? this.props.className : ''},
this.getParsedDate()
);
}
}
TimeAgo.defaultProps = {
element: 'p',
date: new Date(),
className: undefined,
isLive: true,
addSuffix: true,
includeSeconds: true,
};
const allowedDateTypes = [
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Date)
];
TimeAgo.propTypes = {
element: React.PropTypes.string,
date: React.PropTypes.oneOfType(allowedDateTypes),
className: React.PropTypes.string,
isLive: React.PropTypes.bool,
addSuffix: React.PropTypes.bool,
includeSeconds: React.PropTypes.bool
};
export default TimeAgo;
@AleksandrChernyavenko
Copy link

Thank you !

@AleksandrChernyavenko
Copy link

Need clear timer to prevent memory leak.

    componentWillUnmount() {
        if (this.timeInterval) {
            clearInterval(this.timeInterval)
        }
    }

    updateTime = () => {
        const interval = this.getInterval();
        if (interval > 0) {
            this.timeInterval = setTimeout(this.updateTime, interval); // <<<<<<------------ this line
            this.forceUpdate();
        }
    }

@amcdnl
Copy link

amcdnl commented May 25, 2022

For others out there, I made this a little more modular and FC:

import { differenceInSeconds, formatDistance } from 'date-fns';

export function getDifference(date: Date) {
  return differenceInSeconds(new Date(), date)
}

export interface FormatRelativeOptions {
  addSuffix?: boolean;
  includeSeconds?: boolean;
}

export function formatRelative(date: Date, options?: FormatRelativeOptions) {
  const diff = getDifference(date);

  if (diff < 30) {
    return 'now';
  } else {
    return formatDistance(date, new Date(), options);
  }
}

export function getInterval(date: Date) {
  const diff = getDifference(date);

  if (diff < 3600) {
    return 60000;
  } else if (diff >= 3600 && diff <= 86400) {
    return 3600000;
  } else {
    return 0;
  }
}

then:

  const dateFormatted = dateFnFormat(dateObj, format);
  const [relative, setRelative] = useState<string>(formatRelative(dateObj, { includeSeconds, addSuffix }));
  const timeout = useRef<any | null>(null);

  const updateTime = useCallback(() => {
    if (isRelative) {
      clearTimeout(timeout.current);

      const interval = getInterval(dateObj);
      if (interval > 0) {
        timeout.current = setTimeout(() => {
          setRelative(formatRelative(dateObj, { includeSeconds, addSuffix }));
        }, interval);
      }
    }
  }, [isRelative, dateObj, includeSeconds, addSuffix]);

  useEffect(() => {
    updateTime();

    const cur = timeout.current;
    return () => clearTimeout(cur);
  });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment