@@ -20,14 +20,22 @@ export function AnimatedColorBlocks() {
2020 const [ blocks , setBlocks ] = useState < BlockState [ ] > (
2121 COLORS . map ( ( ) => ( { opacity : prefersReducedMotion ? 1 : 0 , transitioning : false } ) )
2222 )
23+ const timers = useRef < ReturnType < typeof setTimeout > [ ] > ( [ ] )
2324 const mounted = useRef ( true )
2425
26+ function schedule ( fn : ( ) => void , ms : number ) {
27+ const id = setTimeout ( fn , ms )
28+ timers . current . push ( id )
29+ return id
30+ }
31+
2532 useEffect ( ( ) => {
2633 mounted . current = true
34+ timers . current = [ ]
2735 if ( prefersReducedMotion ) return
2836
2937 COLORS . forEach ( ( _ , i ) => {
30- setTimeout ( ( ) => {
38+ schedule ( ( ) => {
3139 if ( ! mounted . current ) return
3240 setBlocks ( ( prev ) =>
3341 prev . map ( ( b , idx ) => ( idx === i ? { opacity : 1 , transitioning : true } : b ) )
@@ -36,22 +44,23 @@ export function AnimatedColorBlocks() {
3644 } )
3745
3846 const totalEnterMs = COLORS . length * ENTER_STAGGER_MS + ENTER_DURATION_MS + HOLD_MS
39- const cycleTimer = setTimeout ( ( ) => {
47+ schedule ( ( ) => {
4048 if ( ! mounted . current ) return
4149 startCycle ( )
4250 } , totalEnterMs )
4351
4452 return ( ) => {
4553 mounted . current = false
46- clearTimeout ( cycleTimer )
54+ timers . current . forEach ( clearTimeout )
55+ timers . current = [ ]
4756 }
4857 } , [ prefersReducedMotion ] )
4958
5059 function startCycle ( ) {
5160 if ( ! mounted . current ) return
5261
5362 COLORS . forEach ( ( _ , i ) => {
54- setTimeout ( ( ) => {
63+ schedule ( ( ) => {
5564 if ( ! mounted . current ) return
5665 setBlocks ( ( prev ) =>
5766 prev . map ( ( b , idx ) => ( idx === i ? { opacity : 0.15 , transitioning : true } : b ) )
@@ -60,10 +69,10 @@ export function AnimatedColorBlocks() {
6069 } )
6170
6271 const exitTotalMs = COLORS . length * EXIT_STAGGER_MS + EXIT_DURATION_MS
63- setTimeout ( ( ) => {
72+ schedule ( ( ) => {
6473 if ( ! mounted . current ) return
6574 COLORS . forEach ( ( _ , i ) => {
66- setTimeout ( ( ) => {
75+ schedule ( ( ) => {
6776 if ( ! mounted . current ) return
6877 setBlocks ( ( prev ) =>
6978 prev . map ( ( b , idx ) =>
@@ -76,7 +85,7 @@ export function AnimatedColorBlocks() {
7685
7786 const cycleDuration =
7887 exitTotalMs + 200 + COLORS . length * ENTER_STAGGER_MS + ENTER_DURATION_MS + HOLD_MS
79- setTimeout ( ( ) => startCycle ( ) , cycleDuration )
88+ schedule ( ( ) => startCycle ( ) , cycleDuration )
8089 }
8190
8291 return (
@@ -104,16 +113,18 @@ export function AnimatedColorBlocksVertical() {
104113 transitioning : false ,
105114 } ) )
106115 )
116+ const timers = useRef < ReturnType < typeof setTimeout > [ ] > ( [ ] )
107117 const mounted = useRef ( true )
108118
109119 useEffect ( ( ) => {
110120 mounted . current = true
121+ timers . current = [ ]
111122 if ( prefersReducedMotion ) return
112123
113124 const baseDelay = COLORS . length * ENTER_STAGGER_MS + 100
114125
115126 COLORS . slice ( 0 , 3 ) . forEach ( ( _ , i ) => {
116- setTimeout (
127+ const id = setTimeout (
117128 ( ) => {
118129 if ( ! mounted . current ) return
119130 setBlocks ( ( prev ) =>
@@ -122,10 +133,13 @@ export function AnimatedColorBlocksVertical() {
122133 } ,
123134 baseDelay + i * ENTER_STAGGER_MS
124135 )
136+ timers . current . push ( id )
125137 } )
126138
127139 return ( ) => {
128140 mounted . current = false
141+ timers . current . forEach ( clearTimeout )
142+ timers . current = [ ]
129143 }
130144 } , [ prefersReducedMotion ] )
131145
0 commit comments