fix: 修复App.tsx中useEffect依赖死循环导致题目不断自动刷新的问题

This commit is contained in:
2026-08-17 09:23:55 +08:00
parent 91c69a5413
commit 747d1faf7a
+51 -34
View File
@@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useRef, useCallback } from 'react';
import questionsData from './questions.json'; import questionsData from './questions.json';
import './index.css'; import './index.css';
@@ -23,11 +23,19 @@ export default function App() {
const [totalAnswered, setTotalAnswered] = useState(0); const [totalAnswered, setTotalAnswered] = useState(0);
const [isSyncing, setIsSyncing] = useState(false); const [isSyncing, setIsSyncing] = useState(false);
// 使用 ref 避免因 weights 变化触发 pickRandomQuestion 重新创建导致死循环
const weightsRef = useRef<Record<number, number>>({});
const updateWeights = useCallback((newWeights: Record<number, number>) => {
weightsRef.current = newWeights;
setWeights(newWeights);
}, []);
// 随机挑选一道题目(基于权重轮盘赌算法) // 随机挑选一道题目(基于权重轮盘赌算法)
const pickRandomQuestion = useCallback((latestWeights?: Record<number, number>) => { const pickRandomQuestion = useCallback((latestWeights?: Record<number, number>) => {
if (questions.length === 0) return; if (questions.length === 0) return;
const currentWeights = latestWeights || weights; const currentWeights = latestWeights || weightsRef.current;
let totalWeight = 0; let totalWeight = 0;
questions.forEach(q => { questions.forEach(q => {
totalWeight += (currentWeights[q.id] || 1); totalWeight += (currentWeights[q.id] || 1);
@@ -47,38 +55,47 @@ export default function App() {
setCurrentQuestion(selectedQ); setCurrentQuestion(selectedQ);
setSelectedOptions([]); setSelectedOptions([]);
setIsSubmitted(false); setIsSubmitted(false);
}, [questions, weights]);
// 从服务端拉取全局统计与权重数据
const fetchGlobalStats = useCallback(async () => {
try {
const res = await fetch('/api/stats');
if (res.ok) {
const data = await res.json();
if (data.weights) setWeights(data.weights);
if (data.history) setHistory(data.history);
if (typeof data.totalAnswered === 'number') setTotalAnswered(data.totalAnswered);
return data.weights;
}
} catch (e) {
console.warn('获取全局统计数据失败,使用本地兜底:', e);
}
// 兜底本地逻辑
const initialWeights: Record<number, number> = {};
questions.forEach(q => {
initialWeights[q.id] = 1;
});
setWeights(initialWeights);
return initialWeights;
}, [questions]); }, [questions]);
// 初始化加载 // 从服务端拉取全局统计与权重数据并初始化
useEffect(() => { useEffect(() => {
fetchGlobalStats().then((loadedWeights) => { let isMounted = true;
pickRandomQuestion(loadedWeights);
}); async function init() {
}, [fetchGlobalStats, pickRandomQuestion]); let loadedWeights: Record<number, number> = {};
try {
const res = await fetch('/api/stats');
if (res.ok) {
const data = await res.json();
if (data.weights) loadedWeights = data.weights;
if (isMounted) {
if (data.history) setHistory(data.history);
if (typeof data.totalAnswered === 'number') setTotalAnswered(data.totalAnswered);
}
}
} catch (e) {
console.warn('获取全局统计数据失败,使用本地兜底:', e);
}
if (Object.keys(loadedWeights).length === 0) {
questions.forEach(q => {
loadedWeights[q.id] = 1;
});
}
if (isMounted) {
updateWeights(loadedWeights);
pickRandomQuestion(loadedWeights);
}
}
init();
return () => {
isMounted = false;
};
}, [questions, updateWeights, pickRandomQuestion]);
const handleOptionClick = (optionLabel: string) => { const handleOptionClick = (optionLabel: string) => {
if (isSubmitted) return; if (isSubmitted) return;
@@ -111,10 +128,10 @@ export default function App() {
setHistory(localNewHistory); setHistory(localNewHistory);
setTotalAnswered(prev => prev + 1); setTotalAnswered(prev => prev + 1);
const localWeights = { ...weights }; const localWeights = { ...weightsRef.current };
const currentWeight = localWeights[currentQuestion.id] || 1; const currentWeight = localWeights[currentQuestion.id] || 1;
localWeights[currentQuestion.id] = isCorrect ? Math.max(1, currentWeight - 1) : currentWeight + 3; localWeights[currentQuestion.id] = isCorrect ? Math.max(1, currentWeight - 1) : currentWeight + 3;
setWeights(localWeights); updateWeights(localWeights);
// 异步同步到后端服务端 // 异步同步到后端服务端
setIsSyncing(true); setIsSyncing(true);
@@ -132,7 +149,7 @@ export default function App() {
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
if (data.weights) { if (data.weights) {
setWeights(data.weights); updateWeights(data.weights);
nextWeights = data.weights; nextWeights = data.weights;
} }
if (data.history) setHistory(data.history); if (data.history) setHistory(data.history);