Skip to content

Commit cd5f070

Browse files
committed
fix: clean up event listeners and timers in React hooks
1 parent 8523fcc commit cd5f070

2 files changed

Lines changed: 19 additions & 5 deletions

File tree

client/src/components/ui/CopyButton.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from 'react';
1+
import { useState, useRef, useEffect } from 'react';
22
import { Copy, Check } from 'lucide-react';
33
import toast from 'react-hot-toast';
44

@@ -11,6 +11,11 @@ interface CopyButtonProps {
1111

1212
export function CopyButton({ text, label = 'Copied!', size = 14, className = '' }: CopyButtonProps) {
1313
const [copied, setCopied] = useState(false);
14+
const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
15+
16+
useEffect(() => {
17+
return () => clearTimeout(timeoutRef.current);
18+
}, []);
1419

1520
const handleCopy = async () => {
1621
if (!navigator?.clipboard?.writeText) {
@@ -21,7 +26,8 @@ export function CopyButton({ text, label = 'Copied!', size = 14, className = ''
2126
if (success) {
2227
setCopied(true);
2328
toast.success(label);
24-
setTimeout(() => setCopied(false), 2000);
29+
clearTimeout(timeoutRef.current);
30+
timeoutRef.current = setTimeout(() => setCopied(false), 2000);
2531
} else {
2632
toast.error('Failed to copy to clipboard');
2733
}

client/src/hooks/useSSE.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,24 @@ export function useSSE(
2727
// Proxy every named event through the ref so the EventSource instance
2828
// stays stable while handler implementations can change freely.
2929
const names = Object.keys(handlersRef.current);
30+
const listeners: Array<[string, (e: Event) => void]> = [];
3031
for (const name of names) {
3132
if (name === 'message') {
3233
eventSource.onmessage = (e) => handlersRef.current.message?.(e);
3334
} else {
34-
eventSource.addEventListener(name, (e) => {
35+
const listener = (e: Event) => {
3536
handlersRef.current[name]?.(e as MessageEvent);
36-
});
37+
};
38+
eventSource.addEventListener(name, listener);
39+
listeners.push([name, listener]);
3740
}
3841
}
3942

40-
return () => eventSource.close();
43+
return () => {
44+
for (const [name, listener] of listeners) {
45+
eventSource.removeEventListener(name, listener);
46+
}
47+
eventSource.close();
48+
};
4149
}, [url]);
4250
}

0 commit comments

Comments
 (0)