Fix AMS slot modal infinite scroll loop on Windows

Inline callback refs on preset buttons called scrollIntoView on every
re-render, creating a scroll → re-render → scrollIntoView loop on
Windows. Replace with a useEffect + data attribute approach that only
scrolls when the selected preset actually changes.
This commit is contained in:
AneoPsy
2026-03-03 00:00:47 -10:00
parent 3c52b5e739
commit d048d72df2
@@ -1,4 +1,4 @@
import { useState, useMemo, useEffect, useCallback } from 'react';
import { useState, useMemo, useEffect, useCallback, useRef } from 'react';
import { useQuery, useMutation } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { X, Loader2, Settings2, ChevronDown, CheckCircle2, RotateCcw } from 'lucide-react';
@@ -242,6 +242,7 @@ export function ConfigureAmsSlotModal({
const [searchQuery, setSearchQuery] = useState('');
const [showSuccess, setShowSuccess] = useState(false);
const [showExtendedColors, setShowExtendedColors] = useState(false);
const scrolledToRef = useRef<string>('');
// Fetch cloud settings (gracefully handle 401 when logged out)
const { data: cloudSettings, isLoading: settingsLoading, isError: cloudError } = useQuery({
@@ -735,6 +736,7 @@ export function ConfigureAmsSlotModal({
setColorInput('');
setSearchQuery('');
setShowSuccess(false);
scrolledToRef.current = '';
}
}, [isOpen, slotInfo.savedPresetId, slotInfo.trayInfoIdx, slotInfo.trayColor, cloudSettings?.filament]);
@@ -770,6 +772,17 @@ export function ConfigureAmsSlotModal({
}
}, [isOpen, handleKeyDown]);
// Scroll selected preset into view only when the selection changes (not on every render).
// Inline callback refs cause scrollIntoView to fire every render, which on Windows
// creates an infinite scroll loop due to scroll → re-render → scrollIntoView cycles.
useEffect(() => {
if (selectedPresetId && selectedPresetId !== scrolledToRef.current) {
scrolledToRef.current = selectedPresetId;
const el = document.querySelector(`[data-preset-id="${CSS.escape(selectedPresetId)}"]`);
el?.scrollIntoView({ block: 'nearest' });
}
}, [selectedPresetId]);
if (!isOpen) return null;
const isLoading = (settingsLoading && !cloudError) || localLoading || builtinLoading || kprofilesLoading;
@@ -889,9 +902,7 @@ export function ConfigureAmsSlotModal({
filteredPresets.map((preset) => (
<button
key={preset.id}
ref={selectedPresetId === preset.id ? (el) => {
el?.scrollIntoView({ block: 'nearest' });
} : undefined}
data-preset-id={preset.id}
onClick={() => setSelectedPresetId(preset.id)}
className={`w-full p-2 rounded-lg border text-left transition-colors ${
selectedPresetId === preset.id
@@ -1126,9 +1137,7 @@ export function ConfigureAmsSlotModal({
filteredPresets.map((preset) => (
<button
key={preset.id}
ref={selectedPresetId === preset.id ? (el) => {
el?.scrollIntoView({ block: 'nearest' });
} : undefined}
data-preset-id={preset.id}
onClick={() => setSelectedPresetId(preset.id)}
className={`w-full p-2 rounded-lg border text-left transition-colors ${
selectedPresetId === preset.id