From cee399ed1513d32d0fff51a6f63898aa861605e1 Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Thu, 23 Oct 2025 23:05:30 -0500 Subject: [PATCH] fix(card-sorting): use empty deps array for useSprings to prevent recreation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added empty dependency array to useSprings and track animation state with ref to ensure springs are only created once and never reset. Changes: - Add empty deps array to useSprings (prevents recreation) - Track animation completion with hasAnimatedRef - If already animated, start new springs at grid positions - Only run animation effect once This ensures cards stay in grid positions through all re-renders. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../card-sorting/components/ResultsPhase.tsx | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/apps/web/src/arcade-games/card-sorting/components/ResultsPhase.tsx b/apps/web/src/arcade-games/card-sorting/components/ResultsPhase.tsx index 9bdf54f2..515f1850 100644 --- a/apps/web/src/arcade-games/card-sorting/components/ResultsPhase.tsx +++ b/apps/web/src/arcade-games/card-sorting/components/ResultsPhase.tsx @@ -106,21 +106,41 @@ export function ResultsPhase() { } } - // Create springs for each card - use useMemo to prevent recreation on every render + // Create springs for each card - memoize to prevent recreation const initialPositions = useMemo(() => { return userSequence.map((card) => getInitialPosition(card.id)) }, []) // Empty deps - only calculate once on mount - const [springs, api] = useSprings(userSequence.length, (index) => { - return { - from: initialPositions[index], - to: initialPositions[index], - config: config.gentle, - } - }) + // Track if animation has completed + const hasAnimatedRef = useRef(false) + + const [springs, api] = useSprings( + userSequence.length, + (index) => { + // If already animated, start at grid position + if (hasAnimatedRef.current) { + const card = userSequence[index] + const correctIndex = state.correctOrder.findIndex((c) => c.id === card.id) + return { + from: calculateGridPosition(correctIndex), + to: calculateGridPosition(correctIndex), + config: config.gentle, + } + } + // Otherwise start at initial position + return { + from: initialPositions[index], + to: initialPositions[index], + config: config.gentle, + } + }, + [] // Empty deps - only create once + ) // Immediately start animating to grid positions useEffect(() => { + if (hasAnimatedRef.current) return // Don't animate again + // Small delay to ensure mount const timer = setTimeout(() => { api.start((index) => { @@ -131,9 +151,10 @@ export function ResultsPhase() { config: { ...config.gentle, tension: 120, friction: 26 }, } }) + hasAnimatedRef.current = true }, 100) return () => clearTimeout(timer) - }, [api, userSequence, state.correctOrder]) + }, []) // Show corrections after animation completes useEffect(() => {