refactor(editor): untrack() fuer form-lokale Snapshots (Item I)
Some checks failed
Build & Publish Docker Image / build-and-push (push) Has been cancelled

Alle 10 pre-existing svelte-check WARNINGs ("state_referenced_locally")
in RecipeEditor.svelte und recipes/[id]/+page.svelte addressiert.

Die betroffenen `let foo = $state(recipe.bar)`-Pattern sind
intentional Snapshots: der Editor soll User-Edits behalten und nicht
von prop-Updates ueberschrieben werden. untrack() macht die Intent
explizit und silenced die Warnung sauber statt sie unter den Teppich
zu kehren.

Scope: imagePath, title, description, servings, prepMin, cookMin,
totalMin, ingredients, steps (RecipeEditor) + recipeState
(recipes/[id]/+page).

Gate: svelte-check 0 Warnings (war 10), Tests 184/184.

Refs docs/superpowers/plans/2026-04-19-post-review-roadmap.md Item I.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-19 11:38:35 +02:00
parent 9ee8efa479
commit 5a1ffee3bb
2 changed files with 21 additions and 16 deletions

View File

@@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import { untrack } from 'svelte';
import { Plus, Trash2, ChevronUp, ChevronDown, ImagePlus, ImageOff } from 'lucide-svelte'; import { Plus, Trash2, ChevronUp, ChevronDown, ImagePlus, ImageOff } from 'lucide-svelte';
import type { Recipe, Ingredient, Step } from '$lib/types'; import type { Recipe, Ingredient, Step } from '$lib/types';
import { alertAction, confirmAction } from '$lib/client/confirm.svelte'; import { alertAction, confirmAction } from '$lib/client/confirm.svelte';
@@ -25,7 +26,7 @@
let { recipe, saving = false, onsave, oncancel, onimagechange }: Props = $props(); let { recipe, saving = false, onsave, oncancel, onimagechange }: Props = $props();
let imagePath = $state<string | null>(recipe.image_path); let imagePath = $state<string | null>(untrack(() => recipe.image_path));
let uploading = $state(false); let uploading = $state(false);
let fileInput: HTMLInputElement | null = $state(null); let fileInput: HTMLInputElement | null = $state(null);
@@ -94,12 +95,14 @@
} }
} }
let title = $state(recipe.title); // Form-lokaler Zustand: Initialwerte aus dem Prop snapshotten (untrack),
let description = $state(recipe.description ?? ''); // damit User-Edits nicht von prop-Updates ueberschrieben werden.
let servings = $state<number | ''>(recipe.servings_default ?? ''); let title = $state(untrack(() => recipe.title));
let prepMin = $state<number | ''>(recipe.prep_time_min ?? ''); let description = $state(untrack(() => recipe.description ?? ''));
let cookMin = $state<number | ''>(recipe.cook_time_min ?? ''); let servings = $state<number | ''>(untrack(() => recipe.servings_default ?? ''));
let totalMin = $state<number | ''>(recipe.total_time_min ?? ''); let prepMin = $state<number | ''>(untrack(() => recipe.prep_time_min ?? ''));
let cookMin = $state<number | ''>(untrack(() => recipe.cook_time_min ?? ''));
let totalMin = $state<number | ''>(untrack(() => recipe.total_time_min ?? ''));
type DraftIng = { type DraftIng = {
qty: string; qty: string;
@@ -110,15 +113,17 @@
type DraftStep = { text: string }; type DraftStep = { text: string };
let ingredients = $state<DraftIng[]>( let ingredients = $state<DraftIng[]>(
recipe.ingredients.map((i) => ({ untrack(() =>
qty: i.quantity !== null ? String(i.quantity).replace('.', ',') : '', recipe.ingredients.map((i) => ({
unit: i.unit ?? '', qty: i.quantity !== null ? String(i.quantity).replace('.', ',') : '',
name: i.name, unit: i.unit ?? '',
note: i.note ?? '' name: i.name,
})) note: i.note ?? ''
}))
)
); );
let steps = $state<DraftStep[]>( let steps = $state<DraftStep[]>(
recipe.steps.map((s) => ({ text: s.text })) untrack(() => recipe.steps.map((s) => ({ text: s.text })))
); );
function addIngredient() { function addIngredient() {

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { onMount, onDestroy, tick } from 'svelte'; import { onMount, onDestroy, tick, untrack } from 'svelte';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { import {
@@ -41,7 +41,7 @@
let editMode = $state(false); let editMode = $state(false);
let saving = $state(false); let saving = $state(false);
let recipeState = $state(data.recipe); let recipeState = $state(untrack(() => data.recipe));
// Einmalige Pulse-Animation beim Aktivieren (nicht beim Wieder-Abwählen). // Einmalige Pulse-Animation beim Aktivieren (nicht beim Wieder-Abwählen).
// Per tick()-Zwischenschritt "aus → an" erzwingen, damit die Animation // Per tick()-Zwischenschritt "aus → an" erzwingen, damit die Animation