UNPKG

react-countup

Version:

A React component wrapper around CountUp.js

198 lines (167 loc) 4.77 kB
import Count from 'countup.js'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import warning from 'warning'; class CountUp extends Component { static propTypes = { decimal: PropTypes.string, decimals: PropTypes.number, delay: PropTypes.number, easingFn: PropTypes.func, end: PropTypes.number.isRequired, formattingFn: PropTypes.func, onEnd: PropTypes.func, onStart: PropTypes.func, prefix: PropTypes.string, redraw: PropTypes.bool, separator: PropTypes.string, start: PropTypes.number, suffix: PropTypes.string, style: PropTypes.object, useEasing: PropTypes.bool, }; static defaultProps = { decimal: '.', decimals: 0, delay: null, duration: null, easingFn: null, formattingFn: null, onEnd: () => {}, onPauseResume: () => {}, onReset: () => {}, onStart: () => {}, onUpdate: () => {}, prefix: '', redraw: false, separator: '', start: 0, suffix: '', style: undefined, useEasing: true, }; componentDidMount() { const { children, delay } = this.props; this.instance = this.createInstance(); // Don't invoke start if component is used as a render prop if (typeof children === 'function' && delay !== 0) return; // Otherwise just start immediately this.start(); } shouldComponentUpdate(nextProps) { const hasCertainPropsChanged = this.props.duration !== nextProps.duration || this.props.end !== nextProps.end || this.props.start !== nextProps.start; return hasCertainPropsChanged || this.props.redraw; } componentDidUpdate(prevProps) { // If duration or start has changed, there's no way to update the duration // or start value. So we need to re-create the CountUp instance in order to // restart it. if ( this.props.duration !== prevProps.duration || this.props.start !== prevProps.start ) { this.instance = this.createInstance(); this.start(); } // Only end value has changed, so reset and and re-animate with the updated // end value. if (this.props.end !== prevProps.end) { this.instance.reset(); this.instance.update(this.props.end); } } createInstance = () => { if (typeof this.props.children === 'function') { // Warn when user didn't use containerRef at all warning( this.containerRef.current && this.containerRef.current instanceof HTMLElement, `Couldn't find attached element to hook the CountUp instance into! Try to attach "containerRef" from the render prop to a an HTMLElement, eg. <span ref={containerRef} />.`, ); } const { decimal, decimals, duration, easingFn, end, formattingFn, prefix, separator, start, suffix, useEasing, } = this.props; return new Count( this.containerRef.current, start, end, decimals, duration, { decimal, easingFn, formattingFn, separator, prefix, suffix, useEasing, useGrouping: !!separator, }, ); }; pauseResume = () => { const { reset, restart: start, update } = this; const { onPauseResume } = this.props; this.instance.pauseResume(); onPauseResume({ reset, start, update }); }; reset = () => { const { pauseResume, restart: start, update } = this; const { onReset } = this.props; this.instance.reset(); onReset({ pauseResume, start, update }); }; restart = () => { this.reset(); this.start(); }; start = () => { const { pauseResume, reset, restart: start, update } = this; const { delay, onEnd, onStart } = this.props; const run = () => this.instance.start(() => onEnd({ pauseResume, reset, start, update })); // Delay start if delay prop is properly set if (delay > 0) { setTimeout(run, delay * 1000); } else { run(); } onStart({ pauseResume, reset, update }); }; update = newEnd => { const { pauseResume, reset, restart: start } = this; const { onUpdate } = this.props; this.instance.update(newEnd); onUpdate({ pauseResume, reset, start }); }; containerRef = React.createRef(); render() { const { children, className, style } = this.props; const { containerRef, pauseResume, reset, restart, update } = this; if (typeof children === 'function') { return children({ countUpRef: containerRef, pauseResume, reset, start: restart, update, }); } return <span className={className} ref={containerRef} style={style} />; } } export default CountUp;