fix: Use shallow comparison in selector hooks to prevent infinite re-renders

The selectors were creating new object references on every call, causing
Zustand's subscription system to detect changes and trigger infinite
re-render loops. This was particularly evident in usePatternValidationFromStore.

Solution:
- Import useShallow from zustand/react/shallow
- Wrap all selector hooks (usePatternCenter, useUploadedPatternCenter,
  useRotatedBounds, usePatternValidationFromStore) with useShallow
- useShallow performs shallow comparison on returned objects, preventing
  re-renders when values haven't actually changed

This follows the established pattern in the codebase where useShallow is
already used extensively (App.tsx, FileUpload.tsx, etc).

Co-authored-by: jhbruhn <1036566+jhbruhn@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-12-28 08:10:40 +00:00
parent 6fbb3ebf1a
commit bcb5ea1786

View file

@ -1,4 +1,5 @@
import { create } from "zustand";
import { useShallow } from "zustand/react/shallow";
import type { PesPatternData } from "../formats/import/pesImporter";
import { onPatternDeleted } from "./storeEvents";
import { calculatePatternCenter } from "../components/PatternCanvas/patternCanvasHelpers";
@ -293,28 +294,33 @@ export const selectPatternValidation = (
};
/**
* Hook to get pattern center (memoized)
* Hook to get pattern center (memoized with shallow comparison)
*/
export const usePatternCenter = () => usePatternStore(selectPatternCenter);
export const usePatternCenter = () =>
usePatternStore(useShallow(selectPatternCenter));
/**
* Hook to get uploaded pattern center (memoized)
* Hook to get uploaded pattern center (memoized with shallow comparison)
*/
export const useUploadedPatternCenter = () =>
usePatternStore(selectUploadedPatternCenter);
usePatternStore(useShallow(selectUploadedPatternCenter));
/**
* Hook to get rotated bounds (memoized)
* Hook to get rotated bounds (memoized with shallow comparison)
*/
export const useRotatedBounds = () => usePatternStore(selectRotatedBounds);
export const useRotatedBounds = () =>
usePatternStore(useShallow(selectRotatedBounds));
/**
* Hook to get pattern validation result (requires machineInfo)
* Use this with caution as it requires external state
* Uses shallow comparison to prevent infinite re-renders from new object references
*/
export const usePatternValidationFromStore = (
machineInfo: { maxWidth: number; maxHeight: number } | null,
) => usePatternStore((state) => selectPatternValidation(state, machineInfo));
) =>
usePatternStore(
useShallow((state) => selectPatternValidation(state, machineInfo)),
);
// Subscribe to pattern deleted event.
// This subscription is intended to persist for the lifetime of the application,