fix(music): setTimeout 정리 + useCallback 폴링 deps
- TrendsTab: useRef로 타이머 ID 추적 후 언마운트 시 clearTimeout 호출 (stale setState 방지) - VideoProjectsTab: loadProjects를 useCallback으로 감싸고 폴링 useEffect deps에 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// src/pages/music/components/TrendsTab.jsx
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
getLatestTrendReport, getTrendReports,
|
||||
getMarketSuggestions, triggerYoutubeResearch,
|
||||
@@ -28,6 +28,9 @@ export default function TrendsTab() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [researchMsg, setResearchMsg] = useState('');
|
||||
|
||||
const researchTimerRef = useRef(null);
|
||||
const copyTimerRef = useRef(null);
|
||||
|
||||
const loadAll = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
@@ -48,12 +51,20 @@ export default function TrendsTab() {
|
||||
|
||||
useEffect(() => { loadAll(); }, []);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
clearTimeout(researchTimerRef.current);
|
||||
clearTimeout(copyTimerRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleResearch = async () => {
|
||||
setResearching(true);
|
||||
try {
|
||||
await triggerYoutubeResearch();
|
||||
setResearchMsg('수집이 시작되었습니다. 잠시 후 새로고침하세요.');
|
||||
setTimeout(() => setResearchMsg(''), 4000);
|
||||
clearTimeout(researchTimerRef.current);
|
||||
researchTimerRef.current = setTimeout(() => setResearchMsg(''), 4000);
|
||||
} catch (e) {
|
||||
console.error('triggerYoutubeResearch:', e);
|
||||
} finally {
|
||||
@@ -64,7 +75,8 @@ export default function TrendsTab() {
|
||||
const handleCopy = (text, idx) => {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
setCopiedIdx(idx);
|
||||
setTimeout(() => setCopiedIdx(null), 2000);
|
||||
clearTimeout(copyTimerRef.current);
|
||||
copyTimerRef.current = setTimeout(() => setCopiedIdx(null), 2000);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/pages/music/components/VideoProjectsTab.jsx
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import {
|
||||
createVideoProject, getVideoProjects,
|
||||
renderVideoProject, exportVideoProject, deleteVideoProject,
|
||||
@@ -26,14 +26,14 @@ export default function VideoProjectsTab({ library, initialTrackId, onClearIniti
|
||||
}
|
||||
}, [initialTrackId]);
|
||||
|
||||
const loadProjects = async () => {
|
||||
const loadProjects = useCallback(async () => {
|
||||
try {
|
||||
const data = await getVideoProjects();
|
||||
setProjects(Array.isArray(data) ? data : data.projects ?? []);
|
||||
} catch (e) {
|
||||
console.error('getVideoProjects:', e);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => { loadProjects(); }, []);
|
||||
|
||||
@@ -49,7 +49,7 @@ export default function VideoProjectsTab({ library, initialTrackId, onClearIniti
|
||||
return () => {
|
||||
if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null; }
|
||||
};
|
||||
}, [projects]);
|
||||
}, [projects, loadProjects]);
|
||||
|
||||
const toggleCountry = (c) => {
|
||||
setCountries(prev =>
|
||||
|
||||
Reference in New Issue
Block a user