Services: correction crash Versus (find undefined .nom), Versus a afficher, sync likes a la suppression + nettoyages
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# 🔧 Correction — un crash caché + le Versus à finir
|
||||
|
||||
Beau commit : le boss est bien rebranché (tu lis `trilogies` via les props, plus de liste locale en double), le **podium** s'affiche, le bouton **🗑️ supprimer** marche. Trois choses à régler, dans l'ordre d'urgence.
|
||||
|
||||
---
|
||||
|
||||
## 1. 💥 Le bug qui fait planter la page (à corriger en premier)
|
||||
|
||||
Regarde ton bloc Versus, en haut d'`Accueil` :
|
||||
```jsx
|
||||
const [a, setA] = useState(trilogies[0].id);
|
||||
const gagnante =
|
||||
likesA === likesB ? "Égalité"
|
||||
: likesA > likesB ? trilogies.find((t) => t.id === Number(a)).nom
|
||||
: trilogies.find((t) => t.id === Number(b)).nom;
|
||||
```
|
||||
|
||||
Ce calcul tourne **à chaque affichage** de la page. Le piège est dans `.find(...).nom` :
|
||||
|
||||
> `trilogies.find(...)` renvoie la trilogie trouvée… **ou `undefined` si elle n'existe pas**. Et `undefined.nom` plante (« Cannot read properties of undefined »), ce qui fait **écran blanc**.
|
||||
|
||||
Le scénario qui déclenche le crash est juste sous ton nez : `a` vaut l'id de la **1ʳᵉ trilogie** au démarrage. Si tu **supprimes cette trilogie** (avec ton tout nouveau bouton 🗑️), `find` ne la trouve plus → `undefined.nom` → 💥. Ton module Supprimer fait donc planter ton module Versus.
|
||||
|
||||
### ✅ La correction : ne jamais supposer que `find` a trouvé
|
||||
|
||||
Récupère d'abord les trilogies, **puis** vérifie qu'elles existent avant de lire `.nom` :
|
||||
```jsx
|
||||
const triloA = trilogies.find((t) => t.id === Number(a));
|
||||
const triloB = trilogies.find((t) => t.id === Number(b));
|
||||
|
||||
const gagnante =
|
||||
!triloA || !triloB ? "—" // une des deux a été supprimée
|
||||
: likesA === likesB ? "Égalité"
|
||||
: likesA > likesB ? triloA.nom : triloB.nom;
|
||||
```
|
||||
|
||||
> 🔎 Autre écriture possible avec l'« optional chaining » : `trilogies.find(...)?.nom`. Le `?.` veut dire « si c'est `undefined`, ne lis pas `.nom`, renvoie `undefined` au lieu de planter ». Très pratique à retenir.
|
||||
|
||||
> ⚠️ Même piège au montage : `useState(trilogies[1].id)` planterait s'il y avait **moins de 2 trilogies** au démarrage. Avec tes 5 trilogies de départ, c'est OK — mais garde en tête que `trilogies[1]` n'existe pas toujours.
|
||||
|
||||
---
|
||||
|
||||
## 2. Le Versus n'est affiché nulle part
|
||||
|
||||
Tu calcules `a`, `b`, `likesA`, `likesB`, `gagnante`… mais il n'y a **aucun `<select>` ni verdict dans le JSX**, et `setA`/`setB` ne sont jamais appelés. Résultat : du code mort (et des warnings ESLint « `setA` is never used »).
|
||||
|
||||
Deux choix : **finir l'interface**, ou **supprimer le bloc**. Pour le finir, ajoute dans le `return` deux menus + le verdict :
|
||||
```jsx
|
||||
<div className="versus">
|
||||
<select value={a} onChange={(e) => setA(e.target.value)}>
|
||||
{trilogies.map((t) => (
|
||||
<option key={t.id} value={t.id}>{t.nom}</option>
|
||||
))}
|
||||
</select>
|
||||
<select value={b} onChange={(e) => setB(e.target.value)}>
|
||||
{trilogies.map((t) => (
|
||||
<option key={t.id} value={t.id}>{t.nom}</option>
|
||||
))}
|
||||
</select>
|
||||
<p>🏆 Gagnante : {gagnante}</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
> 🔎 La valeur d'un `<select>` est **une chaîne** ("1", "2"…). C'est pour ça que tu fais `Number(a)` dans le `find` : pour comparer un nombre (`t.id`) à un nombre. Garde bien ce `Number(...)`.
|
||||
|
||||
**Test :** change les deux menus → le verdict se met à jour. Supprime une des deux trilogies choisies → ça affiche « — » au lieu de planter (grâce au point 1).
|
||||
|
||||
---
|
||||
|
||||
## 3. Supprimer une trilogie ne retire pas ses likes du total
|
||||
|
||||
Ta fonction supprime bien la trilogie, mais **laisse ses likes** dans l'objet `likes` :
|
||||
```jsx
|
||||
function supprimer(id) {
|
||||
setTrilogies(trilogies.filter((t) => t.id !== id)); // ✅ enlève la trilogie
|
||||
// ❌ mais likes[id] reste → le "Total de likes" ne baisse pas
|
||||
}
|
||||
```
|
||||
|
||||
Comme `total = Object.values(likes).reduce((s, n) => s + n, 0)`, le total continue d'additionner les likes d'une trilogie qui n'existe plus.
|
||||
|
||||
### ✅ La correction : supprimer aussi dans `likes`
|
||||
|
||||
Tu as déjà `setLikes` dans les props, donc c'est direct :
|
||||
```jsx
|
||||
function supprimer(id) {
|
||||
setTrilogies(trilogies.filter((t) => t.id !== id));
|
||||
setLikes((prev) => {
|
||||
const copie = { ...prev }; // une COPIE (on ne modifie jamais l'ancien objet)
|
||||
delete copie[id]; // on retire l'entrée de cette trilogie
|
||||
return copie; // on renvoie le nouvel objet
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
> 🧩 `{ ...prev }` puis `delete` : on copie l'objet, on enlève la clé de la **copie**, on renvoie la copie. Même règle d'**immutabilité** que partout — React ne réaffiche que si on lui donne un **nouvel** objet.
|
||||
|
||||
> 💡 La leçon générale : quand deux états décrivent la même chose (une trilogie **et** ses likes), une action sur l'un doit se répercuter sur l'autre, sinon ils **divergent**.
|
||||
|
||||
**Test :** like une trilogie 3 fois (total = 3) → supprime-la → le total **retombe** à 0, et elle disparaît du podium.
|
||||
|
||||
---
|
||||
|
||||
## 🧹 Petits restes
|
||||
- `console.log("test")` en haut d'`Accueil` → à enlever.
|
||||
- Le fragment `<>...</>` autour du seul `<select>` ne sert à rien → tu peux laisser le `<select>` seul.
|
||||
- **Dark mode** : tu as ajouté le CSS `.sombre` 👍 mais rien ne l'active encore. Il te manque un état `const [sombre, setSombre] = useState(false)`, la classe sur ton `<div className={sombre ? "body sombre" : "body"}>`, et un bouton `onClick={() => setSombre(!sombre)}`. (Détaillé dans `ENONCE_final_boss.md`, module P.)
|
||||
|
||||
> 💡 À retenir du jour : **un `.find` peut ne rien trouver** → ne lis jamais `.quelquechose` dessus sans vérifier (ou avec `?.`). Et garde tes états synchronisés.
|
||||
Reference in New Issue
Block a user