|
1 | | -import { useEffect, useRef } from 'react'; |
2 | 1 | import { useInView, useMotionValue, useSpring } from 'motion/react'; |
| 2 | +import { useCallback, useEffect, useRef } from 'react'; |
3 | 3 |
|
4 | 4 | interface CountUpProps { |
5 | 5 | to: number; |
@@ -52,11 +52,25 @@ export default function CountUp({ |
52 | 52 |
|
53 | 53 | const maxDecimals = Math.max(getDecimalPlaces(from), getDecimalPlaces(to)); |
54 | 54 |
|
| 55 | + const formatValue = useCallback((latest: number) => { |
| 56 | + const hasDecimals = maxDecimals > 0; |
| 57 | + |
| 58 | + const options: Intl.NumberFormatOptions = { |
| 59 | + useGrouping: !!separator, |
| 60 | + minimumFractionDigits: hasDecimals ? maxDecimals : 0, |
| 61 | + maximumFractionDigits: hasDecimals ? maxDecimals : 0 |
| 62 | + }; |
| 63 | + |
| 64 | + const formattedNumber = Intl.NumberFormat('en-US', options).format(latest); |
| 65 | + |
| 66 | + return separator ? formattedNumber.replace(/,/g, separator) : formattedNumber; |
| 67 | + }, [maxDecimals, separator]) |
| 68 | + |
55 | 69 | useEffect(() => { |
56 | 70 | if (ref.current) { |
57 | | - ref.current.textContent = String(direction === 'down' ? to : from); |
| 71 | + ref.current.textContent = formatValue(direction === 'down' ? to : from); |
58 | 72 | } |
59 | | - }, [from, to, direction]); |
| 73 | + }, [from, to, direction, formatValue]); |
60 | 74 |
|
61 | 75 | useEffect(() => { |
62 | 76 | if (isInView && startWhen) { |
@@ -85,24 +99,14 @@ export default function CountUp({ |
85 | 99 | }, [isInView, startWhen, motionValue, direction, from, to, delay, onStart, onEnd, duration]); |
86 | 100 |
|
87 | 101 | useEffect(() => { |
88 | | - const unsubscribe = springValue.on('change', latest => { |
| 102 | + const unsubscribe = springValue.on('change', (latest: number) => { |
89 | 103 | if (ref.current) { |
90 | | - const hasDecimals = maxDecimals > 0; |
91 | | - |
92 | | - const options: Intl.NumberFormatOptions = { |
93 | | - useGrouping: !!separator, |
94 | | - minimumFractionDigits: hasDecimals ? maxDecimals : 0, |
95 | | - maximumFractionDigits: hasDecimals ? maxDecimals : 0 |
96 | | - }; |
97 | | - |
98 | | - const formattedNumber = Intl.NumberFormat('en-US', options).format(latest); |
99 | | - |
100 | | - ref.current.textContent = separator ? formattedNumber.replace(/,/g, separator) : formattedNumber; |
| 104 | + ref.current.textContent = formatValue(latest); |
101 | 105 | } |
102 | 106 | }); |
103 | 107 |
|
104 | 108 | return () => unsubscribe(); |
105 | | - }, [springValue, separator, maxDecimals]); |
| 109 | + }, [springValue, formatValue]); |
106 | 110 |
|
107 | 111 | return <span className={className} ref={ref} />; |
108 | 112 | } |
0 commit comments