ThemeToggle: lint react-hooks/set-state-in-effect (hydratation localStorage) #90
Labels
No labels
autopilot:pending-human
source:analyste
source:defenseur
source:human
source:medic
status:approved
status:blocked
status:in-progress
status:needs-clarification
status:needs-fix
status:ready
status:review
status:triage
type:bug
type:feature
type:infra
type:refactor
type:schema
type:security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: maximus/simpl-liste#90
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Contexte
web/src/components/ThemeToggle.tsx:13-16declenche l'erreur eslintreact-hooks/set-state-in-effect: lecture delocalStorageau mount puissetTheme(stored)synchrone dans unuseEffect, ce qui force un double-render.Analyse (suite a /analyze 90)
Cartographie du systeme de theme (sous-agent Explore) :
ThemeScript.tsx(inline<script>dans<head>, monte dansapp/layout.tsx:34) pose la classedarksurdocument.documentElementavant hydratation : litsl-theme, resoutsystemviamatchMedia. Le flash de fond de page (FOUC) est donc deja couvert.<html suppressHydrationWarning>(layout.tsx:31) masque deja le mismatch d'hydratation.sl-themecoherente partout (ThemeScript + ThemeToggle). Aucun mismatch de cle.[theme]) n'est pas flagge : c'est l'usage autorise du rule (synchroniser un systeme externe avec l'etat React). Seul le 1er effect (read -> setState) est en cause.web/src/-> pas de candidat hook partage, fix local.useSyncExternalStore, pas deuseMounted/useIsClient, pas de gardetypeof window.storage), non requis.web/(scripts : dev/build/start/lint).Note : framing initial corrige
L'issue disait que
useSyncExternalStore"supprime le double-render". Inexact : le serveur ne peut pas lirelocalStorage, doncgetServerSnapshotretourne"system"-> une correction au montage reste de toute facon, et le flash d'icone Monitor -> Moon n'est pas garanti supprime. Le seul vrai fix UX du flash serait un theme lisible au SSR (cookie). Conclusion : ni le disable niuseSyncExternalStoren'eliminent le micro-flash d'icone ; seul un theme en cookie le ferait.Decision : Option A — eslint-disable documente
Choisie via /analyze (2026-05-30). Le FOUC de page est deja resolu par ThemeScript ; le residuel est uniquement l'icone du toggle, acceptable pour cette dette incidente.
useSyncExternalStore(option B) n'ameliore pas franchement l'UX pour le cout et n'a aucun precedent ; le theme-en-cookie (option C) a un scope trop large (migration du theming entier) pour cette issue.Options B et C documentees ci-dessus si on veut revenir dessus plus tard.
Travail a faire
ThemeToggle.tsx, 1eruseEffect, ajouter un// eslint-disable-next-line react-hooks/set-state-in-effectcible sur la lignesetTheme+ un commentaire justificatif, p.ex. :eslintweb/ vert (0 erreur)tsc --noEmitcleanFichiers concernes
web/src/components/ThemeToggle.tsx— seul fichier (1er useEffect)Surface de test
web/)type:refactor) -> verification manuelle visuelleeslint+tsc --noEmitCriteres d'acceptation
eslintdeweb/: 0 erreurComplexite
Simple.
Decouvert lors du nettoyage lint de
web/(suite a #70). Les 3 lint triviaux corriges dans PR #91.