Merge pull request #47 from jhbruhn/fix/30-eslint-hook-rules-useCanvasViewport

fix: Refactor useCanvasViewport to eliminate ESLint hook warnings
This commit is contained in:
Jan-Henrik Bruhn 2025-12-26 21:55:07 +01:00 committed by GitHub
commit 3ec9dda235
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -5,13 +5,7 @@
* Handles wheel zoom and button zoom operations * Handles wheel zoom and button zoom operations
*/ */
import { import { useState, useEffect, useCallback, type RefObject } from "react";
useState,
useEffect,
useCallback,
useRef,
type RefObject,
} from "react";
import type Konva from "konva"; import type Konva from "konva";
import type { PesPatternData } from "../formats/import/pesImporter"; import type { PesPatternData } from "../formats/import/pesImporter";
import type { MachineInfo } from "../types/machine"; import type { MachineInfo } from "../types/machine";
@ -34,8 +28,11 @@ export function useCanvasViewport({
const [stagePos, setStagePos] = useState({ x: 0, y: 0 }); const [stagePos, setStagePos] = useState({ x: 0, y: 0 });
const [stageScale, setStageScale] = useState(1); const [stageScale, setStageScale] = useState(1);
const [containerSize, setContainerSize] = useState({ width: 0, height: 0 }); const [containerSize, setContainerSize] = useState({ width: 0, height: 0 });
const initialScaleRef = useRef<number>(1); const [initialScale, setInitialScale] = useState(1);
const prevPesDataRef = useRef<PesPatternData | null>(null);
// Track the last processed pattern to detect changes during render
const [lastProcessedPattern, setLastProcessedPattern] =
useState<PesPatternData | null>(null);
// Track container size with ResizeObserver // Track container size with ResizeObserver
useEffect(() => { useEffect(() => {
@ -59,41 +56,36 @@ export function useCanvasViewport({
return () => resizeObserver.disconnect(); return () => resizeObserver.disconnect();
}, [containerRef]); }, [containerRef]);
// Calculate and store initial scale when pattern or hoop changes // Reset viewport when pattern changes (during render, not in effect)
useEffect(() => { // This follows the React-recommended pattern for deriving state from props
// Use whichever pattern is available (uploaded or original) const currentPattern = uploadedPesData || pesData;
const currentPattern = uploadedPesData || pesData; if (
if (!currentPattern || containerSize.width === 0) { currentPattern &&
prevPesDataRef.current = null; currentPattern !== lastProcessedPattern &&
return; containerSize.width > 0
} ) {
const { bounds } = currentPattern;
const viewWidth = machineInfo
? machineInfo.maxWidth
: bounds.maxX - bounds.minX;
const viewHeight = machineInfo
? machineInfo.maxHeight
: bounds.maxY - bounds.minY;
// Only recalculate if pattern changed const newInitialScale = calculateInitialScale(
if (prevPesDataRef.current !== currentPattern) { containerSize.width,
prevPesDataRef.current = currentPattern; containerSize.height,
viewWidth,
viewHeight,
);
const { bounds } = currentPattern; // Update state during render when pattern changes
const viewWidth = machineInfo // This is the recommended React pattern for resetting state based on props
? machineInfo.maxWidth setLastProcessedPattern(currentPattern);
: bounds.maxX - bounds.minX; setInitialScale(newInitialScale);
const viewHeight = machineInfo setStageScale(newInitialScale);
? machineInfo.maxHeight setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 });
: bounds.maxY - bounds.minY; }
const initialScale = calculateInitialScale(
containerSize.width,
containerSize.height,
viewWidth,
viewHeight,
);
initialScaleRef.current = initialScale;
// Reset view when pattern changes
// eslint-disable-next-line react-hooks/set-state-in-effect
setStageScale(initialScale);
setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 });
}
}, [pesData, uploadedPesData, machineInfo, containerSize]);
// Wheel zoom handler // Wheel zoom handler
const handleWheel = useCallback((e: Konva.KonvaEventObject<WheelEvent>) => { const handleWheel = useCallback((e: Konva.KonvaEventObject<WheelEvent>) => {
@ -159,10 +151,9 @@ export function useCanvasViewport({
}, [containerSize]); }, [containerSize]);
const handleZoomReset = useCallback(() => { const handleZoomReset = useCallback(() => {
const initialScale = initialScaleRef.current;
setStageScale(initialScale); setStageScale(initialScale);
setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 }); setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 });
}, [containerSize]); }, [initialScale, containerSize]);
return { return {
// State // State