fix(home): $effect-Loop bei sort=viewed via untrack
Der Profile-Switch-Refetch-Effect las allLoading in der sync tracking- Phase. Der await fetch beendete die Sync-Phase, das finale allLoading = false im finally lief ausserhalb → wurde als externer Write interpretiert → Effect rerun → naechster Fetch → Endlosschleife. 2136 GETs auf /api/recipes/all?sort=viewed in 8s beobachtet. Fix: nur profileStore.active bleibt tracked (der tatsaechliche Trigger). allSort/allLoading werden in untrack() gelesen — die Writes auf allLoading im finally triggern damit keinen Effect-Rerun mehr. Verifiziert lokal: 1 Request statt 2000+ bei mount mit allSort=viewed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, tick } from 'svelte';
|
import { onMount, tick, untrack } from 'svelte';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { CookingPot, X, ChevronDown } from 'lucide-svelte';
|
import { CookingPot, X, ChevronDown } from 'lucide-svelte';
|
||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition';
|
||||||
@@ -235,12 +235,17 @@
|
|||||||
// profiles, refetch with the new profile_id so the list reflects what
|
// profiles, refetch with the new profile_id so the list reflects what
|
||||||
// the *current* profile has viewed. Other sorts are profile-agnostic
|
// the *current* profile has viewed. Other sorts are profile-agnostic
|
||||||
// and don't need this.
|
// and don't need this.
|
||||||
|
//
|
||||||
|
// Only `profileStore.active` must be a tracked dep. `allSort` /
|
||||||
|
// `allLoading` are read inside untrack: otherwise the `allLoading = false`
|
||||||
|
// write in the fetch-finally would re-trigger the effect and start the
|
||||||
|
// next fetch → endless loop.
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const active = profileStore.active;
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
|
profileStore.active;
|
||||||
|
untrack(() => {
|
||||||
if (allSort !== 'viewed') return;
|
if (allSort !== 'viewed') return;
|
||||||
if (allLoading) return;
|
if (allLoading) return;
|
||||||
// Re-fetch the first page; rehydrate would re-load the previous
|
|
||||||
// depth, but a sort-context change should reset to page 1 anyway.
|
|
||||||
void (async () => {
|
void (async () => {
|
||||||
allLoading = true;
|
allLoading = true;
|
||||||
try {
|
try {
|
||||||
@@ -253,10 +258,9 @@
|
|||||||
} finally {
|
} finally {
|
||||||
allLoading = false;
|
allLoading = false;
|
||||||
}
|
}
|
||||||
// 'active' is referenced so $effect tracks it as a dep:
|
|
||||||
void active;
|
|
||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Sync current query back into the URL as ?q=... via replaceState,
|
// Sync current query back into the URL as ?q=... via replaceState,
|
||||||
// without spamming the history stack. Pushing a new entry happens only
|
// without spamming the history stack. Pushing a new entry happens only
|
||||||
|
|||||||
Reference in New Issue
Block a user