import * as _ from "lodash"; import * as React from "react"; import { CountUp } from "../../vendor/countup"; import { newRandom } from "../../utilities/numbers"; interface ICounterProps { count: number; title?: string; countUp?: boolean; } export class Counter extends React.PureComponent { public ref: React.RefObject; public counter: CountUp; constructor(props) { super(props); this.ref = React.createRef(); } public componentDidMount() { this.withNode(node => { const initialCount = this.props.countUp ? this.props.count - 20000 : this.props.count; this.counter = new CountUp(node, initialCount, { duration: 2, }); this.counter.start(); }); if (this.props.countUp) { setTimeout(() => { setInterval(() => { this.updateCounter(this.getNewValue()); }, 1000); }, 2500); } } public componentDidUpdate(prevProps: ICounterProps) { if (prevProps.count !== this.props.count) { this.updateCounter(this.props.count); } } public render() { return (
{this.props.title && ( {this.props.title} )}
); } private withNode(found: (node: HTMLDivElement) => T, notFound?: () => T): T { const counterNode = this.ref.current; if (counterNode) { return found(counterNode); } else if (notFound) { return notFound(); } return null; } private getNewValue = (): number => { // @ts-ignore const currentValue = this.counter.endVal; // Guard against reaching limit if (currentValue === this.props.count) return currentValue; const newValue = currentValue + newRandom(10, currentValue); // Guard against overflow if (newValue >= this.props.count) return this.props.count; // Guard against NaN if (!_.isNumber(newValue)) return this.props.count; return newValue; } private updateCounter = (num: number) => { // Guard against no counter if (_.isNil(this.counter)) return; // Guard against a bad num if (!_.isNumber(num) || _.isNaN(num)) return; this.counter.update(num); } }