Fix React Hooks violation: move useMemo calls before early returns
The variationPositionInfo and theoreticalMoves useMemo hooks were called after conditional early returns, violating React's Rules of Hooks. Moved both hooks to be called unconditionally before any early returns. Added null checks inside the hooks to handle missing session/tree safely.
This commit is contained in:
@@ -276,6 +276,44 @@ export default function OpeningTrainer({
|
|||||||
setLastTutorMessageMoveIndex(moveCount);
|
setLastTutorMessageMoveIndex(moveCount);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Family mode hooks - MUST be called before any early returns
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Family mode: compute current position info from variation tree
|
||||||
|
// IMPORTANT: Always call useMemo, even if not in family mode (React Hooks rule)
|
||||||
|
const variationPositionInfo = useMemo(() => {
|
||||||
|
if (!isFamilyMode || !variationTree || !session) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const positionDesc = describeCurrentPosition(variationTree, session.moveHistory);
|
||||||
|
const currentVariations = identifyCurrentVariation(variationTree, session.moveHistory);
|
||||||
|
const possibleMoves = getAllPossibleNextMoves(variationTree, session.moveHistory);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...positionDesc,
|
||||||
|
currentVariations,
|
||||||
|
possibleMoves,
|
||||||
|
isInAnyVariation: positionDesc.matchingCount > 0,
|
||||||
|
};
|
||||||
|
}, [isFamilyMode, variationTree, session]);
|
||||||
|
|
||||||
|
// Get all possible next moves (for display and tutor context)
|
||||||
|
const theoreticalMoves = useMemo(() => {
|
||||||
|
if (isFamilyMode && variationPositionInfo) {
|
||||||
|
return variationPositionInfo.nextMoves;
|
||||||
|
}
|
||||||
|
// Single variation mode
|
||||||
|
if (!session) return [];
|
||||||
|
const moves = parseMoveSequence(opening.moves);
|
||||||
|
const nextMove = moves[session.moveHistory.length];
|
||||||
|
return nextMove ? [nextMove] : [];
|
||||||
|
}, [isFamilyMode, variationPositionInfo, opening.moves, session]);
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Early returns for loading/error states
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
// Session recovery dialog
|
// Session recovery dialog
|
||||||
if (showRecoveryDialog && existingSession) {
|
if (showRecoveryDialog && existingSession) {
|
||||||
return (
|
return (
|
||||||
@@ -384,33 +422,7 @@ export default function OpeningTrainer({
|
|||||||
const lastUserMove = userMoves.length > 0 ? userMoves[userMoves.length - 1] : null;
|
const lastUserMove = userMoves.length > 0 ? userMoves[userMoves.length - 1] : null;
|
||||||
const lastTutorMove = tutorMoves.length > 0 ? tutorMoves[tutorMoves.length - 1] : null;
|
const lastTutorMove = tutorMoves.length > 0 ? tutorMoves[tutorMoves.length - 1] : null;
|
||||||
|
|
||||||
// Family mode: compute current position info from variation tree
|
// Note: variationPositionInfo and theoreticalMoves are computed above (before early returns)
|
||||||
const variationPositionInfo = useMemo(() => {
|
|
||||||
if (!isFamilyMode || !variationTree) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const positionDesc = describeCurrentPosition(variationTree, session.moveHistory);
|
|
||||||
const currentVariations = identifyCurrentVariation(variationTree, session.moveHistory);
|
|
||||||
const possibleMoves = getAllPossibleNextMoves(variationTree, session.moveHistory);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...positionDesc,
|
|
||||||
currentVariations,
|
|
||||||
possibleMoves,
|
|
||||||
isInAnyVariation: positionDesc.matchingCount > 0,
|
|
||||||
};
|
|
||||||
}, [isFamilyMode, variationTree, session.moveHistory]);
|
|
||||||
|
|
||||||
// Get all possible next moves (for display and tutor context)
|
|
||||||
const theoreticalMoves = useMemo(() => {
|
|
||||||
if (isFamilyMode && variationPositionInfo) {
|
|
||||||
return variationPositionInfo.nextMoves;
|
|
||||||
}
|
|
||||||
// Single variation mode
|
|
||||||
const moves = parseMoveSequence(opening.moves);
|
|
||||||
const nextMove = moves[session.moveHistory.length];
|
|
||||||
return nextMove ? [nextMove] : [];
|
|
||||||
}, [isFamilyMode, variationPositionInfo, opening.moves, session.moveHistory.length]);
|
|
||||||
|
|
||||||
const openingPracticeMode = {
|
const openingPracticeMode = {
|
||||||
openingName: opening.name,
|
openingName: opening.name,
|
||||||
|
|||||||
Reference in New Issue
Block a user