@@ -5,13 +5,6 @@ import {
55 DEFAULT_ARRAY_SIZE ,
66} from '../constants' ;
77
8- /**
9- * Custom hook for managing visualization state and animation playback
10- * @param {number[] } initialArray - The initial array to visualize
11- * @param {number } speed - Animation base speed in milliseconds (from ANIMATION_SPEEDS)
12- * @param {string } mode - Visualization mode ('autoplay' or 'manual')
13- * @returns {Object } Visualization state and controls
14- */
158export function useVisualization (
169 initialArray ,
1710 speed ,
@@ -32,23 +25,28 @@ export function useVisualization(
3225 const stepsRef = useRef ( [ ] ) ;
3326 const autoplayTimeoutRef = useRef ( null ) ;
3427
35- // keep stepsRef in sync
28+ // Centralized cleanup function
29+ const clearAutoplayTimeout = useCallback ( ( ) => {
30+ if ( autoplayTimeoutRef . current ) {
31+ clearTimeout ( autoplayTimeoutRef . current ) ;
32+ autoplayTimeoutRef . current = null ;
33+ }
34+ } , [ ] ) ;
35+
36+ // Keep stepsRef in sync
3637 useEffect ( ( ) => {
3738 stepsRef . current = steps ;
3839 } , [ steps ] ) ;
3940
40- // cleanup on unmount: clear any pending timeout
41+ // Cleanup on unmount
4142 useEffect ( ( ) => {
4243 return ( ) => {
43- if ( autoplayTimeoutRef . current ) {
44- clearTimeout ( autoplayTimeoutRef . current ) ;
45- autoplayTimeoutRef . current = null ;
46- }
44+ clearAutoplayTimeout ( ) ;
4745 animationRef . current = null ;
4846 } ;
49- } , [ ] ) ;
47+ } , [ clearAutoplayTimeout ] ) ;
5048
51- // Reset when array changes (external array update)
49+ // Reset when array changes
5250 useEffect ( ( ) => {
5351 setArray ( initialArray ) ;
5452 setStates ( Array ( initialArray . length ) . fill ( ELEMENT_STATES . DEFAULT ) ) ;
@@ -57,41 +55,35 @@ export function useVisualization(
5755 setIsComplete ( false ) ;
5856 setIsPlaying ( false ) ;
5957 setIsAutoplayActive ( false ) ;
60- if ( autoplayTimeoutRef . current ) {
61- clearTimeout ( autoplayTimeoutRef . current ) ;
62- autoplayTimeoutRef . current = null ;
63- }
64- } , [ initialArray ] ) ;
65-
66- const loadSteps = useCallback ( algorithmSteps => {
67- setSteps ( algorithmSteps ) ;
68- setCurrentStep ( 0 ) ;
69- setIsComplete ( false ) ;
58+ clearAutoplayTimeout ( ) ;
59+ } , [ initialArray , clearAutoplayTimeout ] ) ;
60+
61+ const loadSteps = useCallback (
62+ algorithmSteps => {
63+ clearAutoplayTimeout ( ) ;
64+ setSteps ( algorithmSteps ) ;
65+ setCurrentStep ( 0 ) ;
66+ setIsComplete ( false ) ;
7067
71- if ( algorithmSteps . length > 0 ) {
72- const firstStep = algorithmSteps [ 0 ] ;
73- setArray ( firstStep . array ) ;
74- setStates ( firstStep . states ) ;
75- setDescription ( firstStep . description ) ;
76- }
77- } , [ ] ) ;
68+ if ( algorithmSteps . length > 0 ) {
69+ const firstStep = algorithmSteps [ 0 ] ;
70+ setArray ( firstStep . array ) ;
71+ setStates ( firstStep . states ) ;
72+ setDescription ( firstStep . description ) ;
73+ }
74+ } ,
75+ [ clearAutoplayTimeout ]
76+ ) ;
7877
79- /**
80- * computeEffectiveDelay
81- * - scales baseDelay inversely with array length so big arrays don't take forever
82- * - caps total run time to MAX_TOTAL_MS when possible
83- */
8478 const computeEffectiveDelay = ( baseDelay , arrayLength , totalSteps ) => {
85- const MAX_TOTAL_MS = 30000 ; // max total autoplay time default (30s)
86- const MIN_DELAY_MS = 30 ; // lower bound per step
87- const MAX_DELAY_MS = 10000 ; // upper bound per step
79+ const MAX_TOTAL_MS = 30000 ;
80+ const MIN_DELAY_MS = 30 ;
81+ const MAX_DELAY_MS = 10000 ;
8882
89- // scale delay by default array size ratio
9083 const scaled = Math . round (
9184 baseDelay * ( DEFAULT_ARRAY_SIZE / Math . max ( 1 , arrayLength ) )
9285 ) ;
9386
94- // enforce a cap so total time isn't ridiculous
9587 let final = scaled ;
9688 if ( totalSteps && final * totalSteps > MAX_TOTAL_MS ) {
9789 final = Math . max ( MIN_DELAY_MS , Math . floor ( MAX_TOTAL_MS / totalSteps ) ) ;
@@ -100,14 +92,10 @@ export function useVisualization(
10092 return Math . max ( MIN_DELAY_MS , Math . min ( final , MAX_DELAY_MS ) ) ;
10193 } ;
10294
103- /**
104- * Starts autoplay animation or performs single step in manual mode
105- */
10695 const play = useCallback ( ( ) => {
10796 if ( stepsRef . current . length === 0 || isComplete ) return ;
10897
10998 if ( mode === VISUALIZATION_MODES . MANUAL ) {
110- // Manual: advance a single step
11199 if ( currentStep < stepsRef . current . length - 1 ) {
112100 const nextStep = currentStep + 1 ;
113101 const step = stepsRef . current [ nextStep ] ;
@@ -124,6 +112,7 @@ export function useVisualization(
124112 }
125113
126114 // Autoplay
115+ clearAutoplayTimeout ( ) ;
127116 setIsPlaying ( true ) ;
128117 setIsAutoplayActive ( true ) ;
129118 animationRef . current = true ;
@@ -132,6 +121,7 @@ export function useVisualization(
132121 if ( ! animationRef . current || stepIndex >= stepsRef . current . length ) {
133122 setIsPlaying ( false ) ;
134123 setIsAutoplayActive ( false ) ;
124+ clearAutoplayTimeout ( ) ;
135125 if ( stepIndex >= stepsRef . current . length ) {
136126 setIsComplete ( true ) ;
137127 }
@@ -148,6 +138,7 @@ export function useVisualization(
148138 setIsComplete ( true ) ;
149139 setIsPlaying ( false ) ;
150140 setIsAutoplayActive ( false ) ;
141+ clearAutoplayTimeout ( ) ;
151142 return ;
152143 }
153144
@@ -156,36 +147,29 @@ export function useVisualization(
156147 const totalSteps = stepsRef . current . length || 0 ;
157148 const effectiveDelay = computeEffectiveDelay ( speed , arrayLen , totalSteps ) ;
158149
159- // clear any existing timeout before scheduling next
160- if ( autoplayTimeoutRef . current ) {
161- clearTimeout ( autoplayTimeoutRef . current ) ;
162- autoplayTimeoutRef . current = null ;
163- }
164-
150+ clearAutoplayTimeout ( ) ;
165151 autoplayTimeoutRef . current = setTimeout ( ( ) => {
166152 runAutoplay ( stepIndex + 1 ) ;
167153 } , effectiveDelay ) ;
168154 } ;
169155
170156 runAutoplay ( currentStep ) ;
171- } , [ currentStep , speed , isComplete , mode , array . length ] ) ;
157+ } , [
158+ currentStep ,
159+ speed ,
160+ isComplete ,
161+ mode ,
162+ array . length ,
163+ clearAutoplayTimeout ,
164+ ] ) ;
172165
173- /**
174- * Pauses autoplay (can be resumed)
175- */
176166 const pause = useCallback ( ( ) => {
177167 animationRef . current = null ;
178168 setIsPlaying ( false ) ;
179169 setIsAutoplayActive ( false ) ;
180- if ( autoplayTimeoutRef . current ) {
181- clearTimeout ( autoplayTimeoutRef . current ) ;
182- autoplayTimeoutRef . current = null ;
183- }
184- } , [ ] ) ;
170+ clearAutoplayTimeout ( ) ;
171+ } , [ clearAutoplayTimeout ] ) ;
185172
186- /**
187- * Resets to initial step
188- */
189173 const reset = useCallback ( ( ) => {
190174 pause ( ) ;
191175 setCurrentStep ( 0 ) ;
0 commit comments