← back to Bertha
kalshi-dash/src/hooks/useAnimatedValue.js
48 lines
import { useState, useEffect, useRef } from 'react';
/**
* useAnimatedValue — smoothly animate between number values using rAF
*
* @param {number} target — the target value to animate toward
* @param {number} duration — animation duration in ms (default: 800)
* @returns {number} — the current interpolated value (use in your render)
*
* Usage:
* const animatedBalance = useAnimatedValue(data?.balance ?? 0);
* // renders smooth transition whenever `data.balance` changes
*/
export default function useAnimatedValue(target, duration = 800) {
const [current, setCurrent] = useState(target);
const fromRef = useRef(target);
const startRef = useRef(null);
const rafRef = useRef(null);
const targetRef = useRef(target);
useEffect(() => {
targetRef.current = target;
fromRef.current = current;
startRef.current = null;
const animate = (ts) => {
if (!startRef.current) startRef.current = ts;
const elapsed = ts - startRef.current;
const t = Math.min(1, elapsed / duration);
// ease-out exponential
const eased = t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
const next = fromRef.current + (targetRef.current - fromRef.current) * eased;
setCurrent(next);
if (t < 1) {
rafRef.current = requestAnimationFrame(animate);
}
};
rafRef.current = requestAnimationFrame(animate);
return () => cancelAnimationFrame(rafRef.current);
}, [target]); // eslint-disable-line react-hooks/exhaustive-deps
return current;
}