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,19 +56,14 @@ 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 (!currentPattern || containerSize.width === 0) { if (
prevPesDataRef.current = null; currentPattern &&
return; currentPattern !== lastProcessedPattern &&
} containerSize.width > 0
) {
// Only recalculate if pattern changed
if (prevPesDataRef.current !== currentPattern) {
prevPesDataRef.current = currentPattern;
const { bounds } = currentPattern; const { bounds } = currentPattern;
const viewWidth = machineInfo const viewWidth = machineInfo
? machineInfo.maxWidth ? machineInfo.maxWidth
@ -80,20 +72,20 @@ export function useCanvasViewport({
? machineInfo.maxHeight ? machineInfo.maxHeight
: bounds.maxY - bounds.minY; : bounds.maxY - bounds.minY;
const initialScale = calculateInitialScale( const newInitialScale = calculateInitialScale(
containerSize.width, containerSize.width,
containerSize.height, containerSize.height,
viewWidth, viewWidth,
viewHeight, viewHeight,
); );
initialScaleRef.current = initialScale;
// Reset view when pattern changes // Update state during render when pattern changes
// eslint-disable-next-line react-hooks/set-state-in-effect // This is the recommended React pattern for resetting state based on props
setStageScale(initialScale); setLastProcessedPattern(currentPattern);
setInitialScale(newInitialScale);
setStageScale(newInitialScale);
setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 }); 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