fix: memory leaks -- add setTimeout cleanup in ChatInput, SocialViewFeedItem, PostCard

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
senke 2026-02-12 21:54:06 +01:00
parent d919f8c7c9
commit 9f7a42cdb5
3 changed files with 35 additions and 12 deletions

View file

@ -1,4 +1,4 @@
import React, { useState, useCallback } from 'react';
import React, { useState, useCallback, useRef, useEffect } from 'react';
import { Card } from '../ui/card';
import { Button } from '../ui/button';
import { Badge } from '../ui/badge';
@ -58,6 +58,15 @@ const PostCardComponent: React.FC<PostCardProps> = ({ post }) => {
const [showComments, setShowComments] = useState(false);
const [showShareModal, setShowShareModal] = useState(false);
const [shareConfirmed, setShareConfirmed] = useState(false);
const likeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const shareTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
return () => {
if (likeTimeoutRef.current) clearTimeout(likeTimeoutRef.current);
if (shareTimeoutRef.current) clearTimeout(shareTimeoutRef.current);
};
}, []);
// Mock comments
const comments: Comment[] = post.recentComments || [
@ -91,14 +100,16 @@ const PostCardComponent: React.FC<PostCardProps> = ({ post }) => {
setLikesCount((prev) => (isLiked ? prev - 1 : prev + 1));
if (!isLiked) {
setLikeAnimating(true);
setTimeout(() => setLikeAnimating(false), 400);
if (likeTimeoutRef.current) clearTimeout(likeTimeoutRef.current);
likeTimeoutRef.current = setTimeout(() => setLikeAnimating(false), 400);
addToast('Liked post');
}
}, [isLiked, addToast]);
const handleShare = useCallback(() => {
setShareConfirmed(true);
setTimeout(() => setShareConfirmed(false), 1500);
if (shareTimeoutRef.current) clearTimeout(shareTimeoutRef.current);
shareTimeoutRef.current = setTimeout(() => setShareConfirmed(false), 1500);
addToast('Shared!', 'success');
}, [addToast]);

View file

@ -1,4 +1,4 @@
import React, { useState, useCallback } from 'react';
import React, { useState, useCallback, useRef, useEffect } from 'react';
import { motion } from 'framer-motion';
import { Card } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
@ -16,19 +16,30 @@ export function SocialViewFeedItem({ track, onPlay }: SocialViewFeedItemProps) {
const [likeCount, setLikeCount] = useState(track.like_count ?? 0);
const [likeAnimating, setLikeAnimating] = useState(false);
const [shareConfirmed, setShareConfirmed] = useState(false);
const likeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const shareTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
return () => {
if (likeTimeoutRef.current) clearTimeout(likeTimeoutRef.current);
if (shareTimeoutRef.current) clearTimeout(shareTimeoutRef.current);
};
}, []);
const handleLike = useCallback(() => {
setIsLiked((prev) => !prev);
setLikeCount((prev) => (isLiked ? prev - 1 : prev + 1));
if (!isLiked) {
setLikeAnimating(true);
setTimeout(() => setLikeAnimating(false), 400);
if (likeTimeoutRef.current) clearTimeout(likeTimeoutRef.current);
likeTimeoutRef.current = setTimeout(() => setLikeAnimating(false), 400);
}
}, [isLiked]);
const handleShare = useCallback(() => {
setShareConfirmed(true);
setTimeout(() => setShareConfirmed(false), 1500);
if (shareTimeoutRef.current) clearTimeout(shareTimeoutRef.current);
shareTimeoutRef.current = setTimeout(() => setShareConfirmed(false), 1500);
}, []);
return (
@ -61,12 +72,10 @@ export function SocialViewFeedItem({ track, onPlay }: SocialViewFeedItemProps) {
</div>
<div className="px-4 pb-4">
<div
className="bg-card p-4 rounded-xl flex items-center gap-4 border border-border hover:border-primary/20 group cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
<button
type="button"
className="appearance-none bg-transparent border-0 p-0 text-left w-full bg-card p-4 rounded-xl flex items-center gap-4 border border-border hover:border-primary/20 group cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
onClick={() => onPlay(track)}
onKeyDown={(e) => e.key === 'Enter' && onPlay(track)}
role="button"
tabIndex={0}
>
<div className="w-16 h-16 rounded-lg overflow-hidden relative">
<img
@ -87,7 +96,7 @@ export function SocialViewFeedItem({ track, onPlay }: SocialViewFeedItemProps) {
<div className="text-xs text-muted-foreground font-mono pr-2">
{track.duration}
</div>
</div>
</button>
</div>
<div className="px-4 py-3 border-t border-border flex gap-2 text-xs text-muted-foreground">

View file

@ -109,6 +109,9 @@ export const ChatInput: React.FC = () => {
} else {
setTyping(false);
}
return () => {
if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
};
}, [message, setTyping]);
return (