Skip to content

Commit ef45577

Browse files
committed
feat : part2e.md translate missing part in french
1 parent bc67e6e commit ef45577

File tree

2 files changed

+269
-9
lines changed

2 files changed

+269
-9
lines changed

src/content/2/fr/part2d.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -698,19 +698,19 @@ Le code de l'état actuel de notre application se trouve sur la branche <i>part2
698698
699699
<div class="tasks">
700700
701-
<h3>Exercices 2.15.-2.18.</h3>
701+
<h3>Exercices 2.12.-2.15.</h3>
702702
703-
<h4>2.15: phonebook, étape7</h4>
703+
<h4>2.12: phonebook, étape7</h4>
704704
705705
Revenons à notre application de répertoire.
706706
707707
Actuellement, les numéros ajoutés au répertoire ne sont pas enregistrés sur un serveur principal. Corrigez cette situation.
708708
709-
<h4>2.16: phonebook, étape8</h4>
709+
<h4>2.13: phonebook, étape8</h4>
710710
711711
Extrayez le code qui gère la communication avec le backend dans son propre module en suivant l'exemple présenté précédemment dans cette partie du support de cours.
712712
713-
<h4>2.17: phonebook étape9</h4>
713+
<h4>2.14: phonebook étape9</h4>
714714
715715
Permettre aux utilisateurs de supprimer des entrées du répertoire. La suppression peut être effectuée via un bouton dédié pour chaque personne dans la liste du répertoire. Vous pouvez confirmer l'action de l'utilisateur en utilisant la méthode [window.confirm](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm) :
716716
@@ -729,7 +729,7 @@ const delete = (id) => {
729729
}
730730
```
731731
732-
<h4>2.18*: phonebook, étape10</h4>
732+
<h4>2.15*: phonebook, étape10</h4>
733733
734734
Modifiez le code de sorte que si un numéro est ajouté à un utilisateur déjà existant, le nouveau numéro remplacera l'ancien numéro. Il est recommandé d'utiliser la méthode HTTP PUT pour mettre à jour le numéro de téléphone.
735735

src/content/2/fr/part2e.md

Lines changed: 264 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,15 +282,15 @@ Le code de la version finale de notre application se trouve sur la branche <i>pa
282282

283283
<div class="tasks">
284284

285-
<h3>Exercices 2.19.-2.20.</h3>
285+
<h3>Exercices 2.16.-2.17.</h3>
286286

287-
<h4>2.19: phonebook, étape11</h4>
287+
<h4>2.16: phonebook, étape11</h4>
288288

289289
Utilisez l'exemple de [message d'erreur amélioré](/en/part2/adding_styles_to_react_app#improved-error-message) de la partie 2 comme guide pour afficher une notification qui dure quelques secondes après l'exécution d'une opération réussie (une personne est ajoutée ou un nombre est modifié) :
290290

291291
![](../../images/2/27e.png)
292292

293-
<h4>2.20*: phonebook, étape12</h4>
293+
<h4>2.17*: phonebook, étape12</h4>
294294

295295
Ouvrez votre application dans deux navigateurs. **Si vous supprimez une personne dans le navigateur 1**, essayer de <i>modifier le numéro de téléphone de la personne</i> dans le navigateur 2, vous obtiendrez le message d'erreur suivant :
296296

@@ -302,6 +302,266 @@ Corrigez le problème selon l'exemple présenté dans [promises et erreurs](/en/
302302

303303
**Note** Même si vous gérez l'exception, le message d'erreur est affiché sur la console.
304304

305-
C'était le dernier exercice de cette partie du cours. Il est temps de transmettre votre code à GitHub et de marquer tous vos exercices terminés dans le [système de soumission d'exercices](https://studies.cs.helsinki.fi/stats/courses/fullstackopen).
305+
</div>
306+
307+
<div class="content">
308+
309+
### Quelques remarques importantes
310+
311+
À la fin de cette partie, vous trouverez quelques exercices plus difficiles. À ce stade, vous pouvez les ignorer s'ils vous semblent trop complexes. Nous y reviendrons plus tard. Quoi qu'il en soit, ce document mérite d'être lu.
312+
313+
Nous avons fait, dans notre application, quelque chose qui masque une source d'erreur très courante.
314+
315+
Nous avons initialisé l'etat de _notes_ à partir d'un tableau vide:
316+
317+
```js
318+
const App = () => {
319+
const [notes, setNotes] = useState([])
320+
321+
// ...
322+
}
323+
```
324+
325+
C'est une valeur initiale tout à fait naturelle, puisque notes constitue un ensemble, c'est-à-dire qu'il y a de nombreuses notes que l'état va stocker.
326+
327+
Si l'état ne devait sauvegarder qu' " une seule chose ", une valeur initiale plus appropriée serait null, ce qui indique qu'il n'y a <i>rien</i> dans l'état au départ. Voyons ce qui se passe si nous utilisons cette valeur initiale :
328+
329+
```js
330+
const App = () => {
331+
const [notes, setNotes] = useState(null) // highlight-line
332+
333+
// ...
334+
}
335+
```
336+
337+
L'application plante:
338+
339+
![console typerror cannot read properties of null via map from App](../../images/2/31a.png)
340+
341+
Le message d'erreur indique la raison et l'emplacement de l'erreur. Le code qui a provoqué le problème est le suivant :
342+
343+
```js
344+
// notesToShow gets the value of notes
345+
const notesToShow = showAll
346+
? notes
347+
: notes.filter(note => note.important)
348+
349+
// ...
350+
351+
{notesToShow.map(note => // highlight-line
352+
<Note key={note.id} note={note} />
353+
)}
354+
```
355+
356+
Le message d'erreur est:
357+
358+
```bash
359+
Cannot read properties of null (reading 'map')
360+
```
361+
362+
La variable _notesToShow_ est d'abord assigné à la valeur de l'état _notes_ puis le code tente d'appeler la méthode _map_ sur un object inexistant, c'est à dire sur _null_.
363+
364+
Pourquoi ?
365+
366+
Le hook d'effet(useEffect) utilise la fonction _setNotes_ pour affecter à _notes_ les données renvoyées par le back-end
367+
368+
```js
369+
useEffect(() => {
370+
noteService
371+
.getAll()
372+
.then(initialNotes => {
373+
setNotes(initialNotes) // highlight-line
374+
})
375+
}, [])
376+
```
377+
378+
Cependant, le problème est que le hook d'effet n'est exécuté qu'<i>après le premier rendu</i>.
379+
Et puisque _notes_ a pour valeur initiale null:
380+
381+
```js
382+
const App = () => {
383+
const [notes, setNotes] = useState(null) // highlight-line
384+
385+
// ...
386+
```
387+
388+
Lors du premier rendu, le code suivant est exécuté :
389+
390+
```js
391+
notesToShow = notes
392+
393+
// ...
394+
395+
notesToShow.map(note => ...)
396+
```
397+
398+
et cela fait planter l'application, car on ne peut pas appeler la méthode _map_ sur une valeur _null_.
399+
400+
En initialisant _notes_ avec un tableau vide, il n'y a pas d'erreur puisqu'il est permis d'utiliser _map_ sur un tableau vide
401+
402+
Ainsi, l'initialisation de l'état a "masqué" le problème lié au fait que les données ne sont pas encore récupérées depuis le backend
403+
404+
une autre manière de contourner le problème aurait été d'utiliser un <i>rendu conditionnel</i> et de retourner null tant que l'état du composant n'est pas correctement initialisé :
405+
406+
```js
407+
const App = () => {
408+
const [notes, setNotes] = useState(null) // highlight-line
409+
// ...
410+
411+
useEffect(() => {
412+
noteService
413+
.getAll()
414+
.then(initialNotes => {
415+
setNotes(initialNotes)
416+
})
417+
}, [])
418+
419+
// do not render anything if notes is still null
420+
// highlight-start
421+
if (!notes) {
422+
return null
423+
}
424+
// highlight-end
425+
426+
// ...
427+
}
428+
```
429+
430+
Donc, lors du premier rendu, rien n'est affiché. Lorsque les notes arrivent du backend, l'effet utilise la fonction _setNotes_ our mettre à jour la valeur de l'état _notes_. Cela provoque un nouveau rendu du composant, et lors de ce second rendu, les notes sont affichées à l'écran.
431+
432+
Cette méthode basée sur le rendu conditionnel convient dans les cas où il est impossible de définir l'état de façon à permettre un rendu initial.
433+
434+
L'autre point auquel il nous faut encore jeter un œil est le second paramètre de useEffect:
435+
436+
```js
437+
useEffect(() => {
438+
noteService
439+
.getAll()
440+
.then(initialNotes => {
441+
setNotes(initialNotes)
442+
})
443+
}, []) // highlight-line
444+
```
445+
446+
Le second paramètre de <em>useEffect</em> sert à [spécifier la fréquence d'exécution de l'effet](https://react.dev/reference/react/useEffect#parameters). Le principe est que l'effet s'exécute systématiquement après le premier rendu du composant <i>et</i> à chaque fois que la valeur de ce second paramètre change.
447+
448+
Si ce second paramètre est un tableau vide <em>[]</em>, son contenu ne change jamais et l'effet n'est exécuté qu'après le premier rendu du composant. C'est exactement ce que l'on souhaite lorsqu'on initialise l'état de l'application depuis le serveur.
449+
450+
Cependant, il existe des situations où l'on veut exécuter cet effet à d'autres moments, par exemple lorsque l'état du composant change de manière particulière.
451+
452+
Considérons l'exemple d'une application simple pour interroger les taux de change via [l'Api de taux de change](https://www.exchangerate-api.com/):
453+
454+
```js
455+
import { useState, useEffect } from 'react'
456+
import axios from 'axios'
457+
458+
const App = () => {
459+
const [value, setValue] = useState('')
460+
const [rates, setRates] = useState({})
461+
const [currency, setCurrency] = useState(null)
462+
463+
useEffect(() => {
464+
console.log('effect run, currency is now', currency)
465+
466+
// skip if currency is not defined
467+
if (currency) {
468+
console.log('fetching exchange rates...')
469+
axios
470+
.get(`https://open.er-api.com/v6/latest/${currency}`)
471+
.then(response => {
472+
setRates(response.data.rates)
473+
})
474+
}
475+
}, [currency])
476+
477+
const handleChange = (event) => {
478+
setValue(event.target.value)
479+
}
480+
481+
const onSearch = (event) => {
482+
event.preventDefault()
483+
setCurrency(value)
484+
}
485+
486+
return (
487+
<div>
488+
<form onSubmit={onSearch}>
489+
currency: <input value={value} onChange={handleChange} />
490+
<button type="submit">exchange rate</button>
491+
</form>
492+
<pre>
493+
{JSON.stringify(rates, null, 2)}
494+
</pre>
495+
</div>
496+
)
497+
}
498+
499+
export default App
500+
```
501+
502+
L'interface utilisateur de l'application comporte un formulaire dont le champ de saisie reçoit le code de la devise désirée. Si la devise existe, l'application affiche ses taux de change par rapport aux autres monnaies :
503+
504+
![navigateur affichant les taux de change avec eur saisi et console indiquant fetching exchange rates](../../images/2/32new.png)
505+
506+
Lorsque l’on appuie sur le bouton, l’application stocke la devise saisie dans l’état _currency_.
507+
508+
Dès que la valeur de _currency_ change, l’application récupère ses taux de change depuis l’API dans la fonction d’effet :
509+
510+
```js
511+
const App = () => {
512+
// ...
513+
const [currency, setCurrency] = useState(null)
514+
515+
useEffect(() => {
516+
console.log('effect run, currency is now', currency)
517+
518+
// skip if currency is not defined
519+
if (currency) {
520+
console.log('fetching exchange rates...')
521+
axios
522+
.get(`https://open.er-api.com/v6/latest/${currency}`)
523+
.then(response => {
524+
setRates(response.data.rates)
525+
})
526+
}
527+
}, [currency]) // highlight-line
528+
// ...
529+
}
530+
```
531+
532+
Le hook useEffect prend désormais _[currency]_ comme second paramètre. La fonction d’effet s’exécute donc après le premier rendu et <i>à chaque fois</i> que la valeur de ce second paramètre _[currency]_ change. Autrement dit, lorsque l’état _currency_ reçoit une nouvelle valeur, le contenu du tableau est mis à jour et la fonction d’effet est relancée.
533+
534+
Il est normal de choisir _null_ comme valeur initiae pour la variable _currency_, puisque _currency_ ne stocke qu'un seul élément. la valeur _null_ indique qu’il n’y a encore rien dans l’état, et il est très simple, à l’aide d’un if, de vérifier si la variable a reçu une valeur. L’effet comporte donc la condition suivante :
535+
536+
```js
537+
if (currency) {
538+
// exchange rates are fetched
539+
}
540+
```
541+
542+
ce qui empêche de requêter les taux de change juste après le premier rendu lorsque la variable _currency_ a encore sa valeur initiale, c’est-à-dire _null_.
543+
544+
Ainsi, si l’utilisateur saisit par exemple <i>eur</i> dans le champ de recherche, l’application utilise Axios pour effectuer une requête HTTP GET vers l’adresse <https://open.er-api.com/v6/latest/eur> et stocke la réponse dans l’état _rates_.
545+
546+
Lorsque l’utilisateur saisit ensuite une autre valeur dans le champ de recherche, par exemple <i>usd</i>, la fonction d’effet est de nouveau exécutée et les taux de change de la nouvelle devise sont récupérés depuis l’API.
547+
548+
La méthode présentée ici pour effectuer les requêtes API peut sembler un peu lourde. Cette application particulière aurait pu être réalisée entièrement sans utiliser useEffect en effectuant les requêtes directement dans le gestionnaire de soumission du formulaire :
549+
550+
```js
551+
const onSearch = (event) => {
552+
event.preventDefault()
553+
axios
554+
.get(`https://open.er-api.com/v6/latest/${value}`)
555+
.then(response => {
556+
setRates(response.data.rates)
557+
})
558+
}
559+
```
560+
561+
Cependant, il existe des situations où cette technique ne fonctionnerait pas. Par exemple, vous <i>pourriez</i> rencontrer un tel cas dans l’exercice 2.20 où l’utilisation de _useEffect_ pourrait apporter une solution. Notez que cela dépend beaucoup de l’approche choisie ; par exemple, la solution modèle n’utilise pas toujours cette astuce.
306562
307563
</div>
564+
565+
566+
567+

0 commit comments

Comments
 (0)