Functions.jsx 컴포넌트 분할: 1,583→460줄 (3훅+8컴포넌트+유틸)
- lottoUtils.jsx: 공통 유틸·상수 추출 (Ball, NumberRow, 통계 헬퍼 등) - hooks/useLottoData.js: 핵심 데이터 로드 (최신회차, 통계, 시뮬레이션, 리포트) - hooks/usePurchases.js: 구매 기록 CRUD - hooks/useManualRecommend.js: 수동 추천 + 히스토리 - components/: MetricBlock, FrequencyChart, PerformanceBanner, ConfidenceRing, CombinedRecommendPanel, ReportPanel, PersonalAnalysisPanel, PurchasePanel 분리 - getReport import 누락 버그 수정 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
75
src/pages/lotto/hooks/useManualRecommend.js
Normal file
75
src/pages/lotto/hooks/useManualRecommend.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { deleteHistory, getHistory, recommend } from '../../../api';
|
||||
import { buildMetricsFromHistory } from '../lottoUtils';
|
||||
|
||||
export default function useManualRecommend() {
|
||||
const [params, setParams] = useState({
|
||||
recent_window: 200, recent_weight: 2.0, avoid_recent_k: 5,
|
||||
});
|
||||
const presets = useMemo(() => [
|
||||
{ name: '기본', recent_window: 200, recent_weight: 2.0, avoid_recent_k: 5 },
|
||||
{ name: '최근 가중치↑', recent_window: 100, recent_weight: 3.0, avoid_recent_k: 10 },
|
||||
{ name: '안전(분산)', recent_window: 300, recent_weight: 1.6, avoid_recent_k: 8 },
|
||||
{ name: '공격(최근)', recent_window: 80, recent_weight: 3.5, avoid_recent_k: 12 },
|
||||
], []);
|
||||
const [result, setResult] = useState(null);
|
||||
const [history, setHistory] = useState([]);
|
||||
const [historyExpanded, setHistoryExpanded] = useState(false);
|
||||
const historyEndRef = useRef(null);
|
||||
const prevHistoryExpandedRef = useRef(false);
|
||||
const [loading, setLoading] = useState({ recommend: false, history: false });
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const historyMetrics = useMemo(() => buildMetricsFromHistory(history), [history]);
|
||||
const visibleHistory = historyExpanded ? history : history.slice(0, 5);
|
||||
|
||||
const refreshHistory = useCallback(async () => {
|
||||
setLoading((s) => ({ ...s, history: true }));
|
||||
setError('');
|
||||
try {
|
||||
const limit = 100; let offset = 0; const allItems = [];
|
||||
while (true) {
|
||||
const data = await getHistory(limit, offset);
|
||||
const items = data.items ?? [];
|
||||
allItems.push(...items);
|
||||
if (items.length < limit) break;
|
||||
offset += limit;
|
||||
}
|
||||
setHistory(allItems);
|
||||
} catch (e) { setError(e?.message ?? String(e)); }
|
||||
finally { setLoading((s) => ({ ...s, history: false })); }
|
||||
}, []);
|
||||
|
||||
const onRecommend = useCallback(async () => {
|
||||
setLoading((s) => ({ ...s, recommend: true })); setError('');
|
||||
try { const data = await recommend(params); setResult(data); await refreshHistory(); }
|
||||
catch (e) { setError(e?.message ?? String(e)); }
|
||||
finally { setLoading((s) => ({ ...s, recommend: false })); }
|
||||
}, [params, refreshHistory]);
|
||||
|
||||
const onDelete = useCallback(async (id) => {
|
||||
if (!confirm(`히스토리 #${id}를 삭제할까요?`)) return;
|
||||
setError('');
|
||||
try { await deleteHistory(id); setHistory((prev) => prev.filter((item) => item.id !== id)); }
|
||||
catch (e) { setError(e?.message ?? String(e)); }
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (historyExpanded && !prevHistoryExpandedRef.current) {
|
||||
requestAnimationFrame(() => {
|
||||
historyEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
||||
});
|
||||
}
|
||||
prevHistoryExpandedRef.current = historyExpanded;
|
||||
}, [historyExpanded, visibleHistory.length]);
|
||||
|
||||
useEffect(() => { refreshHistory(); }, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
return {
|
||||
params, setParams, presets,
|
||||
result, history, historyExpanded, setHistoryExpanded,
|
||||
historyEndRef, loading, error, setError,
|
||||
historyMetrics, visibleHistory,
|
||||
refreshHistory, onRecommend, onDelete,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user