修复定时器与动画回调清理不完整问题

This commit is contained in:
2026-03-03 12:27:34 +08:00
parent 45dbac1935
commit 4cd593bc30
5 changed files with 57 additions and 12 deletions
+5 -2
View File
@@ -42,7 +42,11 @@ export default function BlindboxRoomPage() {
const timersRef = useRef<ReturnType<typeof setTimeout>[]>([]);
useEffect(() => {
return () => { timersRef.current.forEach(clearTimeout); };
const timers = timersRef.current;
return () => {
timers.forEach(clearTimeout);
timers.length = 0;
};
}, []);
// Hook: Room
@@ -58,7 +62,6 @@ export default function BlindboxRoomPage() {
input, setInput, submitting, suggestions, suggestionsLoading,
suggestionsSource, poolCount, setPoolCount, myIdeas, drawnHistory,
setDrawnHistory, submitFlash, error, setError, inputRef,
boxControls: ideaBoxControls,
fetchIdeas, fetchSuggestions, refreshSuggestions,
handleSubmit, handleEditIdea, handleDeleteIdea,
} = useBlindboxIdeas(room, profile);
+30 -5
View File
@@ -1,6 +1,6 @@
"use client";
import { useState, useRef, useCallback } from "react";
import { useState, useRef, useCallback, useEffect } from "react";
import { useAnimation } from "framer-motion";
import confetti from "canvas-confetti";
import type { DrawnIdea } from "@/components/BlindboxDrawnHistory";
@@ -23,8 +23,21 @@ export function useBlindboxDraw(
const boxControls = useAnimation();
const confettiAliveRef = useRef(false);
const timersRef = useRef<ReturnType<typeof setTimeout>[]>([]);
const animationFrameRef = useRef<number | null>(null);
const stopConfetti = useCallback(() => {
confettiAliveRef.current = false;
if (animationFrameRef.current !== null) {
cancelAnimationFrame(animationFrameRef.current);
animationFrameRef.current = null;
}
timersRef.current.forEach(clearTimeout);
timersRef.current = [];
if (typeof confetti.reset === "function") confetti.reset();
}, []);
const fireConfetti = useCallback(() => {
stopConfetti();
const colors = ["#a855f7", "#6366f1", "#ec4899", "#f59e0b", "#10b981"];
confetti({ particleCount: 100, spread: 120, origin: { y: 0.4 }, colors, startVelocity: 45, ticks: 250 });
confettiAliveRef.current = true;
@@ -33,10 +46,21 @@ export function useBlindboxDraw(
if (Date.now() > end || !confettiAliveRef.current) return;
confetti({ particleCount: 3, angle: 60, spread: 55, origin: { x: 0, y: 0.6 }, colors, startVelocity: 35, ticks: 150 });
confetti({ particleCount: 3, angle: 120, spread: 55, origin: { x: 1, y: 0.6 }, colors, startVelocity: 35, ticks: 150 });
requestAnimationFrame(frame);
animationFrameRef.current = requestAnimationFrame(frame);
};
timersRef.current.push(setTimeout(frame, 200));
}, []);
timersRef.current.push(
setTimeout(() => {
animationFrameRef.current = requestAnimationFrame(frame);
}, 200),
);
}, [stopConfetti]);
useEffect(
() => () => {
stopConfetti();
},
[stopConfetti],
);
const handleDraw = async () => {
if (poolCount === 0 || !profile || !room) {
@@ -78,10 +102,11 @@ export function useBlindboxDraw(
};
const handleContinue = useCallback(() => {
stopConfetti();
setPhase("pool");
setRevealedIdea(null);
setShowShareCard(false);
}, [setPhase]);
}, [setPhase, stopConfetti]);
return {
revealedIdea,
+9 -1
View File
@@ -1,6 +1,6 @@
"use client";
import { useState, useCallback, useRef } from "react";
import { useState, useCallback, useEffect, useRef } from "react";
import { useAnimation } from "framer-motion";
import { useToast } from "@/hooks/useToast";
import type { MyIdea } from "@/components/BlindboxMyIdeas";
@@ -51,6 +51,14 @@ export function useBlindboxIdeas(room: RoomInfo | null, profile: UserProfile | n
const inputRef = useRef<HTMLInputElement>(null);
const timersRef = useRef<ReturnType<typeof setTimeout>[]>([]);
useEffect(() => {
const timers = timersRef.current;
return () => {
timers.forEach(clearTimeout);
timers.length = 0;
};
}, []);
const fetchIdeas = useCallback(async () => {
if (!room || !profile) return;
try {
+5 -1
View File
@@ -37,7 +37,11 @@ export function useBlindboxRoom(code: string) {
const timersRef = useRef<ReturnType<typeof setTimeout>[]>([]);
useEffect(() => {
return () => { timersRef.current.forEach(clearTimeout); };
const timers = timersRef.current;
return () => {
timers.forEach(clearTimeout);
timers.length = 0;
};
}, []);
useEffect(() => {