fix(web): t-Falle in acht Ladeeffekten entschaerft, RssFeedListForm/SavedSearchBar-Ladefunktionen stabilisiert

Befunde 7-15 der Biome-Regel useExhaustiveDependencies (quick-260921-gof):

- VehicleTable, TenderDetail, DigestIntervalForm, SourceConfigForm,
  favorites-widget: Ersatz-Fehlertext aus useTranslations wird jetzt vor
  dem Effekt/Rueckruf in eine Konstante gezogen und diese Konstante in
  die Abhaengigkeitsliste aufgenommen - `t` selbst kommt nirgends mehr
  in eine Liste. In diesem Projekt ist belegt, dass `t` bei jedem
  Durchlauf eine frische Funktion ist (Testattrappen), eine `t`-
  Abhaengigkeit haette den jeweiligen Mount-Abruf zur Schleife gemacht.
- RssFeedListForm.tsx und SavedSearchBar.tsx: die Ladefunktionen waren
  gewoehnliche Funktionen im Rumpf (bei jedem Durchlauf neu) - jetzt in
  einen stabilen Rueckruf mit der Text-Konstante als einziger
  Abhaengigkeit eingepackt.
- ResultsList.tsx: Befund 8 (t) wie oben, Befund 15 (refreshKey) in den
  Effekt verschoben, der `load` aufruft, statt in `load` selbst zu
  stehen - eine begruendete `biome-ignore`-Zeile (erste im Projekt)
  haelt fest, dass der Auffrisch-Ausloeser der Elternseite ohne diese
  Abhaengigkeit wirkungslos waere.
- Sieben Testdateien um eine Zaehlprobe erweitert: erneutes Zeichnen mit
  unveraenderten Props darf keinen weiteren Abruf ausloesen; ResultsList
  zusaetzlich um eine Probe, dass ein refreshKey-Bump genau einen
  weiteren Abruf ausloest.
- DigestIntervalForm hat keine Testdatei - nur am laufenden System auf
  Meine Quellen geprueft (siehe SUMMARY).
- Wirkungslose eslint-disable-Zeilen fuer diese Regel entfallen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
2026-09-21 12:29:05 +02:00
parent b3f0e3cdcd
commit e2c508cff5
15 changed files with 210 additions and 33 deletions
@@ -559,4 +559,18 @@ describe('FavoritesWidget', () => {
});
});
});
it('quick-260921-gof (Befund 14): erneutes Zeichnen mit gleicher instanceId loest KEINEN weiteren Abruf aus — die Uebersetzungs-Attrappe liefert bei jedem Durchlauf eine frische Funktion, eine Rueckkehr von `t` in die Abhaengigkeitsliste faellt hier sofort auf', async () => {
const { rerender } = render(
<FavoritesWidget instanceId="fav-1" config={{}} isEditMode={false} />,
);
await waitFor(() => expect(mockFetch).toHaveBeenCalledTimes(1));
rerender(
<FavoritesWidget instanceId="fav-1" config={{}} isEditMode={false} />,
);
await waitFor(() => expect(mockFetch).toHaveBeenCalledTimes(1));
});
});
@@ -96,6 +96,11 @@ export function FavoritesWidget({
[favorites],
);
// Befund 14 (quick-260921-gof): Ersatztext vor dem Effekt bestimmt, `t`
// selbst bleibt aussen vor — die Testattrappe liefert bei jedem Durchlauf
// eine frische Funktion, das wuerde den Mount-Abruf zur Schleife machen.
const favoritesErrorText = t('favorites.error');
useEffect(() => {
let cancelled = false;
@@ -106,7 +111,7 @@ export function FavoritesWidget({
const data = await fetchFavorites(instanceId);
if (!cancelled) setFavorites(data);
} catch {
if (!cancelled) setError(t('favorites.error'));
if (!cancelled) setError(favoritesErrorText);
} finally {
if (!cancelled) setLoading(false);
}
@@ -117,10 +122,7 @@ export function FavoritesWidget({
return () => {
cancelled = true;
};
// instanceId is the only meaningful dependency — t is stable at runtime
// and excluded to prevent re-fetching on every render (mock creates new fn)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [instanceId]);
}, [instanceId, favoritesErrorText]);
function getFallbackLetter(title: string): string {
const clean = title.trim();