@@ -29,6 +29,7 @@ export const ReasoningBlock = ({ content, ts, isStreaming, isLast, metadata }: R
2929 const startedAtRef = useRef < number > ( persisted . startedAt ?? Date . now ( ) )
3030 const [ elapsed , setElapsed ] = useState < number > ( persisted . elapsedMs ?? 0 )
3131 const postedRef = useRef < boolean > ( false )
32+ const intervalRef = useRef < number | null > ( null )
3233
3334 // Initialize startedAt on first mount if missing (persist to task) - guard with postedRef
3435 useEffect ( ( ) => {
@@ -49,10 +50,30 @@ export const ReasoningBlock = ({ content, ts, isStreaming, isLast, metadata }: R
4950
5051 const tick = ( ) => setElapsed ( Date . now ( ) - startedAtRef . current )
5152 tick ( )
52- const id = setInterval ( tick , 1000 )
53- return ( ) => clearInterval ( id )
53+ // ensure no duplicate intervals
54+ if ( intervalRef . current ) {
55+ clearInterval ( intervalRef . current )
56+ intervalRef . current = null
57+ }
58+ intervalRef . current = window . setInterval ( tick , 1000 )
59+ return ( ) => {
60+ if ( intervalRef . current ) {
61+ clearInterval ( intervalRef . current )
62+ intervalRef . current = null
63+ }
64+ }
5465 } , [ isLast , isStreaming ] )
5566
67+ // Cleanup on unmount (safety net)
68+ useEffect ( ( ) => {
69+ return ( ) => {
70+ if ( intervalRef . current ) {
71+ clearInterval ( intervalRef . current )
72+ intervalRef . current = null
73+ }
74+ }
75+ } , [ ] )
76+
5677 // Persist final elapsed when streaming stops
5778 const wasActiveRef = useRef < boolean > ( false )
5879 useEffect ( ( ) => {
0 commit comments