Skip to content

Commit a34f392

Browse files
committed
fix: prevent memory leak in ReasoningBlock timer cleanup
1 parent a84d41b commit a34f392

File tree

1 file changed

+23
-2
lines changed

1 file changed

+23
-2
lines changed

webview-ui/src/components/chat/ReasoningBlock.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)