From 22b538662e5dd0759a5252a77870a4fb175b4134 Mon Sep 17 00:00:00 2001 From: linsolas Date: Fri, 28 Apr 2023 18:54:46 +0200 Subject: [PATCH 1/5] doc(custom-hooks): work in progress --- .../learn/reusing-logic-with-custom-hooks.md | 435 +++++++++--------- 1 file changed, 218 insertions(+), 217 deletions(-) diff --git a/src/content/learn/reusing-logic-with-custom-hooks.md b/src/content/learn/reusing-logic-with-custom-hooks.md index 679a9bac2..89445367f 100644 --- a/src/content/learn/reusing-logic-with-custom-hooks.md +++ b/src/content/learn/reusing-logic-with-custom-hooks.md @@ -1,30 +1,30 @@ --- -title: 'Reusing Logic with Custom Hooks' +title: 'Réutilisation de la logique avec des Hooks personnalisés' --- -React comes with several built-in Hooks like `useState`, `useContext`, and `useEffect`. Sometimes, you'll wish that there was a Hook for some more specific purpose: for example, to fetch data, to keep track of whether the user is online, or to connect to a chat room. You might not find these Hooks in React, but you can create your own Hooks for your application's needs. +React intègre plusieurs Hooks tels que `useState`, `useContext` et `useEffect`. Parfois, vous aimeriez qu’il y ait un Hook pour un objectif plus précis : par exemple pour récupérer des données, pour savoir si un utilisateur est en ligne ou pour se connecter à un salon de discussion. Vous ne trouverez peut-être pas ces Hooks dans React, mais vous pouvez créer vos propres Hooks pour les besoins de votre application. -- What custom Hooks are, and how to write your own -- How to reuse logic between components -- How to name and structure your custom Hooks -- When and why to extract custom Hooks +- Que sont les Hooks personnalisés et comment écrire les vôtres +- Comment réutiliser la logique entre composants +- Comment nommer et structurer vos Hooks personnalisés +- Quand et comment extraire des Hooks personnalisés -## Custom Hooks: Sharing logic between components {/*custom-hooks-sharing-logic-between-components*/} +## Hooks personnalisés : partager la logique entre composants {/*custom-hooks-sharing-logic-between-components*/} -Imagine you're developing an app that heavily relies on the network (as most apps do). You want to warn the user if their network connection has accidentally gone off while they were using your app. How would you go about it? It seems like you'll need two things in your component: +Imaginez que vous développez une appli qui repose massivement sur le réseau (comme la plupart des applis le font). Vous souhaitez avertir l’utilisateur si sa connexion au réseau s’est brutalement interrompue pendant qu’il utilisait son appli. Comment feriez-vous cela ? Il semble que vous ayez besoin de deux choses dans votre composant : -1. A piece of state that tracks whether the network is online. -2. An Effect that subscribes to the global [`online`](https://developer.mozilla.org/en-US/docs/Web/API/Window/online_event) and [`offline`](https://developer.mozilla.org/en-US/docs/Web/API/Window/offline_event) events, and updates that state. +1. Un élément d’état qui détermine si le réseau est en ligne ou non. +2. Un effet qui s’abonne aux événements globaux [`online`](https://developer.mozilla.org/fr/docs/Web/API/Window/online_event) et [`offline`](https://developer.mozilla.org/fr/docs/Web/API/Window/offline_event), et met à jour cet état. -This will keep your component [synchronized](/learn/synchronizing-with-effects) with the network status. You might start with something like this: +Cela permettra à votre composant de rester [synchronisé](/learn/synchronizing-with-effects) avec l’état du réseau. Vous pouvez commencer par quelque chose comme ceci : @@ -48,17 +48,17 @@ export default function StatusBar() { }; }, []); - return

{isOnline ? '✅ Online' : '❌ Disconnected'}

; + return

{isOnline ? '✅ En ligne' : '❌ Déconnecté'}

; } ```
-Try turning your network on and off, and notice how this `StatusBar` updates in response to your actions. +Essayez d’activer et de désactiver votre réseau et remarquez comment cette `StatusBar` se met à jour en fonction de vos actions. -Now imagine you *also* want to use the same logic in a different component. You want to implement a Save button that will become disabled and show "Reconnecting..." instead of "Save" while the network is off. +Imaginez maintenant que vous souhaitiez utiliser la *même* logique dans un composant différent. Vous souhaitez créer un bouton Enregistrer qui sera désactivé et affichera « Reconnexion… » au lieu de « Enregistrer » lorsque le réseau est désactivé. -To start, you can copy and paste the `isOnline` state and the Effect into `SaveButton`: +Pour commencer, vous pouvez copier et coller l’état `isOnline` et l’effet dans le `SaveButton` : @@ -83,12 +83,12 @@ export default function SaveButton() { }, []); function handleSaveClick() { - console.log('✅ Progress saved'); + console.log('✅ Progression enregistrée'); } return ( ); } @@ -96,36 +96,36 @@ export default function SaveButton() { -Verify that, if you turn off the network, the button will change its appearance. +Vérifiez que, si vous éteignez le réseau, le bouton changera d’apparence. -These two components work fine, but the duplication in logic between them is unfortunate. It seems like even though they have different *visual appearance,* you want to reuse the logic between them. +Ces deux composants fonctionnent bien, mais la duplication de la logique entre eux est regrettable. Il semble que s’ils ont un *aspect visuel* différent, ils réutilisent la même logique. -### Extracting your own custom Hook from a component {/*extracting-your-own-custom-hook-from-a-component*/} +### Extraire votre Hook personnalisé d’un composant {/*extracting-your-own-custom-hook-from-a-component*/} -Imagine for a moment that, similar to [`useState`](/reference/react/useState) and [`useEffect`](/reference/react/useEffect), there was a built-in `useOnlineStatus` Hook. Then both of these components could be simplified and you could remove the duplication between them: +Imaginez un instant que, comme pour [`useState`](/reference/react/useState) et [`useEffect`](/reference/react/useEffect), il existe un Hook prédéfini `useOnlineStatus`. Ces deux composants pourraient alors être simplifiés et vous pourriez supprimer la duplication entre eux : ```js {2,7} function StatusBar() { const isOnline = useOnlineStatus(); - return

{isOnline ? '✅ Online' : '❌ Disconnected'}

; + return

{isOnline ? '✅ En ligne' : '❌ Déconnecté'}

; } function SaveButton() { const isOnline = useOnlineStatus(); function handleSaveClick() { - console.log('✅ Progress saved'); + console.log('✅ Progression enregistrée'); } return ( ); } ``` -Although there is no such built-in Hook, you can write it yourself. Declare a function called `useOnlineStatus` and move all the duplicated code into it from the components you wrote earlier: +Bien qu'il n'y ait pas de tel Hook intégré, vous pouvez l’écrire vous-même. Déclarez une fonction appelée `useOnlineStatus` et déplacez-y tout le code dupliqué des composants que vous avez écrits plus tôt : ```js {2-16} function useOnlineStatus() { @@ -148,7 +148,7 @@ function useOnlineStatus() { } ``` -At the end of the function, return `isOnline`. This lets your components read that value: +À la fin de la fonction, retournez `isOnline`. Cela permet à votre composant de lire cette valeur : @@ -157,19 +157,19 @@ import { useOnlineStatus } from './useOnlineStatus.js'; function StatusBar() { const isOnline = useOnlineStatus(); - return

{isOnline ? '✅ Online' : '❌ Disconnected'}

; + return

{isOnline ? '✅ En ligne' : '❌ Déconnecté'}

; } function SaveButton() { const isOnline = useOnlineStatus(); function handleSaveClick() { - console.log('✅ Progress saved'); + console.log('✅ Progression enregistrée'); } return ( ); } @@ -209,89 +209,90 @@ export function useOnlineStatus() {
-Verify that switching the network on and off updates both components. +Vérifiez que l’activation et la désactivation du réseau met à jour les deux composants. -Now your components don't have as much repetitive logic. **More importantly, the code inside them describes *what they want to do* (use the online status!) rather than *how to do it* (by subscribing to the browser events).** +Désormais, Vos composants n’ont plus de logique répétitive. **Plus important encore, le code qu'ils contiennent décrivent *ce qu'ils veulent faire* (utiliser le statut de connexion) plut^pt que *la manière de le faire* (en s’abonnant aux événements du navigateur).** -When you extract logic into custom Hooks, you can hide the gnarly details of how you deal with some external system or a browser API. The code of your components expresses your intent, not the implementation. +Quand vous extrayez la logique dans des Hooks personnalisés, vous pouvez cacher les détails de la façon dont vous traitez avec des systèmes externes ou d’une API du navigateur. Le code de vos composants expriment votre intention et pas l’implémentation. -### Hook names always start with `use` {/*hook-names-always-start-with-use*/} +### Les noms des Hooks commencent toujours par `use` {/*hook-names-always-start-with-use*/} -React applications are built from components. Components are built from Hooks, whether built-in or custom. You'll likely often use custom Hooks created by others, but occasionally you might write one yourself! +Les applications React sont constuites à partir de composants. Les composants sont construits à partir des Hooks, qu'ils soient intégrés ou personnalisés. Vous utiliserez probablement souvent des Hooks personnalisés créés par d'autres, mais vous pourrez occasionnellement en écrire un vous-même ! -You must follow these naming conventions: +Vous devez respecter les conventions de nommage suivantes : -1. **React component names must start with a capital letter,** like `StatusBar` and `SaveButton`. React components also need to return something that React knows how to display, like a piece of JSX. -2. **Hook names must start with `use` followed by a capital letter,** like [`useState`](/reference/react/useState) (built-in) or `useOnlineStatus` (custom, like earlier on the page). Hooks may return arbitrary values. +1. **Le nom des composants React doit commencer par une lettre en majuscule,** comme `StatusBar` et `SaveButton`. Les composants React doivent également renvoyer quelque chose que React sait afficher, comme un morceau de JSX. +2. **Le nom des Hook doit commencer par `use` suivi d’une majuscule,** comme [`useState`](/reference/react/useState) (intégré) ou `useOnlineStatus` (personnalisé, comme plus haut dans la page). Les Hooks peuvent renvoyer des valeurs arbitraires. -This convention guarantees that you can always look at a component and know where its state, Effects, and other React features might "hide". For example, if you see a `getColor()` function call inside your component, you can be sure that it can't possibly contain React state inside because its name doesn't start with `use`. However, a function call like `useOnlineStatus()` will most likely contain calls to other Hooks inside! +Cette convention garantit que vous pouvez toujours regarder un composant et savoir où son état, ses effets et d’autres fonctionnalités de React peuvent se « cacher ». Par exemple, si vous voyez un appel à la fonction `getColor()` dans votre composant, vous pouvez être sûr qu’il ne contient pas d’état React car son nom ne commence par par `use`. Cependant, un appel de fonction comme `useOnlineStatus()` contiendra très probablement des appels à d’autres Hooks à l’intérieur. -If your linter is [configured for React,](/learn/editor-setup#linting) it will enforce this naming convention. Scroll up to the sandbox above and rename `useOnlineStatus` to `getOnlineStatus`. Notice that the linter won't allow you to call `useState` or `useEffect` inside of it anymore. Only Hooks and components can call other Hooks! +Si votre linter est [configuré pour React,](/learn/editor-setup#linting) il appliquera cette convention de nommage. Remontez jusqu’au bac à sable et renommez `useOnlineStatus` en `getOnlineStatus`. Notez que le linter ne vous permettra plus appeler `useState` ou `useEffect` à l’intérieur. Seuls les Hooks et les composants peuvent appeler d’autres Hooks ! -#### Should all functions called during rendering start with the use prefix? {/*should-all-functions-called-during-rendering-start-with-the-use-prefix*/} +#### Toutes les fonctions appelées pendant le rendu doivent-elles commencer par le préfixe use ? {/*should-all-functions-called-during-rendering-start-with-the-use-prefix*/} -No. Functions that don't *call* Hooks don't need to *be* Hooks. +Non. Les fonctions qui n’*appelent* pas des Hooks n’ont pas besoin d’*être* des Hooks. -If your function doesn't call any Hooks, avoid the `use` prefix. Instead, write it as a regular function *without* the `use` prefix. For example, `useSorted` below doesn't call Hooks, so call it `getSorted` instead: +Si votre fonction n’appellent aucun Hook, évitez d’utiliser le préfixe `use`. À la place, écrivez une fonction normale *sans* le préfixe `use`. Par exemple, `useSorted` ci-dessous n’appelle pas de Hook, appelez-la `getSorted` à la place : ```js -// 🔴 Avoid: A Hook that doesn't use Hooks +// 🔴 À éviter : un Hook qui n’utilise pas d’autre Hooks. function useSorted(items) { return items.slice().sort(); } -// ✅ Good: A regular function that doesn't use Hooks +// ✅ Correct : une fonction normale qui n’utilise pas de Hook. function getSorted(items) { return items.slice().sort(); } ``` -This ensures that your code can call this regular function anywhere, including conditions: +Cela garantit que votre code peut appeler cette fonction normale n’importe où, y compris dans ces conditions : ```js function List({ items, shouldSort }) { let displayedItems = items; if (shouldSort) { - // ✅ It's ok to call getSorted() conditionally because it's not a Hook + // ✅ Il est possible d’appeler getSorted() conditionnellement parce qu’il ne s’agit pas d’un Hook. displayedItems = getSorted(items); } // ... } ``` -You should give `use` prefix to a function (and thus make it a Hook) if it uses at least one Hook inside of it: +Vous devez utiliser le préfixe `use` pour une fonction (en ainsi en faire un Hook) si elle utilise elle-même un Hook : ```js -// ✅ Good: A Hook that uses other Hooks +// ✅ Correct : un Hook qui utilise un autre Hook function useAuth() { return useContext(Auth); } ``` -Technically, this isn't enforced by React. In principle, you could make a Hook that doesn't call other Hooks. This is often confusing and limiting so it's best to avoid that pattern. However, there may be rare cases where it is helpful. For example, maybe your function doesn't use any Hooks right now, but you plan to add some Hook calls to it in the future. Then it makes sense to name it with the `use` prefix: +Techniquement, cette règle n’est pas dictée par React. En principe, vous pouvez créer un Hook qui n’appelle pas d’autres Hooks. C’est souvent déroutant et limitant, aussi il est préférable d’éviter ce modèle. Cependant, il peut y avoir de rares cas où cela est utile. Par exemple, votre fonction n’appelle pas encore de Hook pour l’instant, mais vous prévoyez d’y ajouter des appels de Hooks dans le futur. Il est alors logique d’utiliser le préfixe `use` : + ```js {3-4} -// ✅ Good: A Hook that will likely use some other Hooks later +// ✅ Correct : un Hook qui utilisera probablement des Hooks par la suite. function useAuth() { - // TODO: Replace with this line when authentication is implemented: + // TODO : remplacer cette ligne quand l’authentification sera implémentée : // return useContext(Auth); return TEST_USER; } ``` -Then components won't be able to call it conditionally. This will become important when you actually add Hook calls inside. If you don't plan to use Hooks inside it (now or later), don't make it a Hook. +Les composants ne pourront pas l’appeler de manière conditionnelle. Cela deviendra important quand vous ajouterez des appels à des Hooks à l’intérieur. Si vous ne prévoyez pas d’appeler des Hooks à l’intérieur (ni maintenant ni plus tard), alors n’en faites pas un Hook. -### Custom Hooks let you share stateful logic, not state itself {/*custom-hooks-let-you-share-stateful-logic-not-state-itself*/} +### Les Hooks personnalisés vous permettent de partager la logique d’état, mais pas l’état lui-même {/*custom-hooks-let-you-share-stateful-logic-not-state-itself*/} -In the earlier example, when you turned the network on and off, both components updated together. However, it's wrong to think that a single `isOnline` state variable is shared between them. Look at this code: +Dans l’exemple précédent, lorsque vous avez activé et désactivé le réseau, les deux composants ont été mis à jour ensemble. Cependant, il est faux de penser qu’une seule variable d’état `isOnline` est partagée entre eux. Regardez ce code : ```js {2,7} function StatusBar() { @@ -305,7 +306,7 @@ function SaveButton() { } ``` -It works the same way as before you extracted the duplication: +Cela fonctionne de la même façon qu’avant la suppression de la duplication : ```js {2-5,10-13} function StatusBar() { @@ -325,9 +326,9 @@ function SaveButton() { } ``` -These are two completely independent state variables and Effects! They happened to have the same value at the same time because you synchronized them with the same external value (whether the network is on). +Il s’agit de deux variables d’état et effets totalement indépendants ! Il se trouve qu’ils ont la même valeur au même moment parce que vous les avez synchronisés entre eux par la même valeur externe (si le réseau est activé). -To better illustrate this, we'll need a different example. Consider this `Form` component: +Pour mieux illustrer ceci, nous allons avoir besoin d’un exemple différent. Considérez ce composant `Form` : @@ -349,14 +350,14 @@ export default function Form() { return ( <> -

Good morning, {firstName} {lastName}.

+

Bonjour, {firstName} {lastName}.

); } @@ -369,13 +370,13 @@ input { margin-left: 10px; }
-There's some repetitive logic for each form field: +Il y a une logique répétitive pour chaque champ du formulaire : -1. There's a piece of state (`firstName` and `lastName`). -1. There's a change handler (`handleFirstNameChange` and `handleLastNameChange`). -1. There's a piece of JSX that specifies the `value` and `onChange` attributes for that input. +1. Il y a un élément de l’état (`firstName` et `lastName`). +1. Il y a un gestionnaire de changement (`handleFirstNameChange` et `handleLastNameChange`). +1. Il y a un morceau de JSX qui spécifie les attributs `value` et `onChange` pour ce champ. -You can extract the repetitive logic into this `useFormInput` custom Hook: +Vous pouvez extraire la logique répétitive dans ce Hook personnalisé `useFormInput` : @@ -389,14 +390,14 @@ export default function Form() { return ( <> -

Good morning, {firstNameProps.value} {lastNameProps.value}.

+

Bonjour, {firstNameProps.value} {lastNameProps.value}.

); } @@ -428,9 +429,9 @@ input { margin-left: 10px; }
-Notice that it only declares *one* state variable called `value`. +Notez qu’il ne déclare qu’*une* seule variable d’état appelée `value`. -However, the `Form` component calls `useFormInput` *two times:* +Cependant, le composant `Form` appelle `useFormInput` *deux fois :* ```js function Form() { @@ -439,17 +440,17 @@ function Form() { // ... ``` -This is why it works like declaring two separate state variables! +C’est pourquoi cela revient à déclarer deux variables d’état distinctes ! -**Custom Hooks let you share *stateful logic* but not *state itself.* Each call to a Hook is completely independent from every other call to the same Hook.** This is why the two sandboxes above are completely equivalent. If you'd like, scroll back up and compare them. The behavior before and after extracting a custom Hook is identical. +**Les Hooks personnalisés vous permettent de partager *la logique d’état* et non *l’état lui-même.* Chaque appel à un Hook est complètement indépendant de tous les autres appels au même Hook.** C’est pourquoi les deux bacs à sable ci-dessus sont totalement équivalents. Si vous le souhaitez, revenez en arrière et comparez-les. Le comportement avant et après l’extraction d’un Hook personnalisé est identique. -When you need to share the state itself between multiple components, [lift it up and pass it down](/learn/sharing-state-between-components) instead. +Lorsque vous avez besoin de partager l’état lui-même entre plusieurs composants, [faites-le remonter puis transmettez-le](/learn/sharing-state-between-components) à la place. -## Passing reactive values between Hooks {/*passing-reactive-values-between-hooks*/} +## Transmettre des valeurs réactives entre les Hooks {/*passing-reactive-values-between-hooks*/} -The code inside your custom Hooks will re-run during every re-render of your component. This is why, like components, custom Hooks [need to be pure.](/learn/keeping-components-pure) Think of custom Hooks' code as part of your component's body! +Le code contenu dans vos Hooks personnalisés sera réexécuté à chaque nouvel affichage de votre composant. C’est pourquoi, comme les composants, les Hooks personnalisés [doivent être purs.](/learn/keeping-components-pure) Considérez le code des Hooks personnalisés comme une partie du corps de votre composant ! -Because custom Hooks re-render together with your component, they always receive the latest props and state. To see what this means, consider this chat room example. Change the server URL or the chat room: +Comme les Hooks personnsalisés sont réaffichés en même temps que votre composant, ils reçoivent toujours les props et l’état les plus récents. Pour comprendre ce que cela signifie, prenez cet exemple de salon de discussion. Changez l’URL du serveur ou le salon de discussion : @@ -462,14 +463,14 @@ export default function App() { return ( <>
@@ -496,7 +497,7 @@ export default function ChatRoom({ roomId }) { }; const connection = createConnection(options); connection.on('message', (msg) => { - showNotification('New message: ' + msg); + showNotification('Nouveau message : ' + msg); }); connection.connect(); return () => connection.disconnect(); @@ -505,10 +506,10 @@ export default function ChatRoom({ roomId }) { return ( <> -

Welcome to the {roomId} room!

+

Bievenue dans le salon {roomId} !

); } @@ -516,18 +517,18 @@ export default function ChatRoom({ roomId }) { ```js chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Une véritable implémentation se connecterait au serveur. if (typeof serverUrl !== 'string') { - throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); + throw Error('serverUrl doit être une chaîne de caractères. Reçu : ' + serverUrl); } if (typeof roomId !== 'string') { - throw Error('Expected roomId to be a string. Received: ' + roomId); + throw Error('roomId doit être une chaîne de caractères. Reçu : ' + roomId); } let intervalId; let messageCallback; return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Connexion au salon "' + roomId + '" depuis ' + serverUrl + '...'); clearInterval(intervalId); intervalId = setInterval(() => { if (messageCallback) { @@ -542,14 +543,14 @@ export function createConnection({ serverUrl, roomId }) { disconnect() { clearInterval(intervalId); messageCallback = null; - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl + ''); + console.log('❌ Déconnexion du salon "' + roomId + '" depuis ' + serverUrl + ''); }, on(event, callback) { if (messageCallback) { - throw Error('Cannot add the handler twice.'); + throw Error('Il n’est pas possible d’ajouter un gestionnaire deux fois.'); } if (event !== 'message') { - throw Error('Only "message" event is supported.'); + throw Error('Seul l’événement "message" est accepté.'); } messageCallback = callback; }, @@ -599,9 +600,9 @@ button { margin-left: 10px; }
-When you change `serverUrl` or `roomId`, the Effect ["reacts" to your changes](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) and re-synchronizes. You can tell by the console messages that the chat re-connects every time that you change your Effect's dependencies. +Quand vous changez `serverUrl` ou `roomId`, l’effet ["réagit" à vos changements](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) et se re-synchronise. Vous pouvez voir dans les messages de la console que le chat se reconnecte à chaque fois que vous changez les dépendances de votre effet. -Now move the Effect's code into a custom Hook: +Maintenant, déplacez le code de l’effet dans un Hook personnalisé : ```js {2-13} export function useChatRoom({ serverUrl, roomId }) { @@ -613,14 +614,14 @@ export function useChatRoom({ serverUrl, roomId }) { const connection = createConnection(options); connection.connect(); connection.on('message', (msg) => { - showNotification('New message: ' + msg); + showNotification('Nouveau message : ' + msg); }); return () => connection.disconnect(); }, [roomId, serverUrl]); } ``` -This lets your `ChatRoom` component call your custom Hook without worrying about how it works inside: +Cela permet à votre composant `ChatRoom` d’appeler le Hook personnalisé sans se préoccuper de la façon dont il fonctionne à l’intérieur. ```js {4-7} export default function ChatRoom({ roomId }) { @@ -634,18 +635,18 @@ export default function ChatRoom({ roomId }) { return ( <> -

Welcome to the {roomId} room!

+

Bienvenue dans le salon {roomId} !

); } ``` -This looks much simpler! (But it does the same thing.) +Ceci semble bien plus simple ! (Mais fait toujours la même chose.) -Notice that the logic *still responds* to prop and state changes. Try editing the server URL or the selected room: +Remarquez que la logique *répond toujours* aux changement des props et de l’état. Essayez de modifier l’URL du serveur ou le salon choisi : @@ -663,9 +664,9 @@ export default function App() { value={roomId} onChange={e => setRoomId(e.target.value)} > - - - + + +
@@ -692,10 +693,10 @@ export default function ChatRoom({ roomId }) { return ( <> -

Welcome to the {roomId} room!

+

Bienvenue dans le salon {roomId} !

); } @@ -715,7 +716,7 @@ export function useChatRoom({ serverUrl, roomId }) { const connection = createConnection(options); connection.connect(); connection.on('message', (msg) => { - showNotification('New message: ' + msg); + showNotification('Nouveau message : ' + msg); }); return () => connection.disconnect(); }, [roomId, serverUrl]); @@ -726,23 +727,23 @@ export function useChatRoom({ serverUrl, roomId }) { export function createConnection({ serverUrl, roomId }) { // A real implementation would actually connect to the server if (typeof serverUrl !== 'string') { - throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); + throw Error('serverUrl doit être une chaîne de caractères. Reçu : ' + serverUrl); } if (typeof roomId !== 'string') { - throw Error('Expected roomId to be a string. Received: ' + roomId); + throw Error('Expected doit être une chaîne de caractères. Reçu : ' + roomId); } let intervalId; let messageCallback; return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Connexion au salon "' + roomId + '" depuis ' + serverUrl + '...'); clearInterval(intervalId); intervalId = setInterval(() => { if (messageCallback) { if (Math.random() > 0.5) { messageCallback('hey') } else { - messageCallback('lol'); + messageCallback('mdr'); } } }, 3000); @@ -750,14 +751,14 @@ export function createConnection({ serverUrl, roomId }) { disconnect() { clearInterval(intervalId); messageCallback = null; - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl + ''); + console.log('❌ Déconnexion du salon "' + roomId + '" depuis ' + serverUrl + ''); }, on(event, callback) { if (messageCallback) { - throw Error('Cannot add the handler twice.'); + throw Error('Il n’est pas possible d’ajouter un gestionnaire deux fois.'); } if (event !== 'message') { - throw Error('Only "message" event is supported.'); + throw Error('Seul l’événement "message" est accepté.'); } messageCallback = callback; }, @@ -807,7 +808,7 @@ button { margin-left: 10px; }
-Notice how you're taking the return value of one Hook: +Remarquez comment vous récupérez la valeur retournée par un Hook : ```js {2} export default function ChatRoom({ roomId }) { @@ -820,7 +821,7 @@ export default function ChatRoom({ roomId }) { // ... ``` -and pass it as an input to another Hook: +puis la transmettre à un autre Hook : ```js {6} export default function ChatRoom({ roomId }) { @@ -833,17 +834,17 @@ export default function ChatRoom({ roomId }) { // ... ``` -Every time your `ChatRoom` component re-renders, it passes the latest `roomId` and `serverUrl` to your Hook. This is why your Effect re-connects to the chat whenever their values are different after a re-render. (If you ever worked with audio or video processing software, chaining Hooks like this might remind you of chaining visual or audio effects. It's as if the output of `useState` "feeds into" the input of the `useChatRoom`.) +Chaque vois que votre composant `ChatRoom` est réaffiché, il passe les dernières valeurs de `roomId` et `serverUrl` à votre Hook. Ceci explique pourquoi votre effet se reconnecte au salon à cahque vois que leurs valeurs sont différentes après un nouveal affichage. (Si vous avez déjà travaillé avec des logiciels de traitement d’audio ou de vidéo, ce type d’enchaînement de Hooks peut vous rappeler l’enchaînement d’effets visuels ou sonores. C’est comme si le retour de `useState` « alimentait » l’entré de `useChatRoom`.) -### Passing event handlers to custom Hooks {/*passing-event-handlers-to-custom-hooks*/} +### Transmettre des gestionnaires d’événements à des Hooks personnalisés {/*passing-event-handlers-to-custom-hooks*/} -This section describes an **experimental API that has not yet been released** in a stable version of React. +Cette section décrit une **API expérimentale qui n’a pas encore été livrée** dans une version stable de React. -As you start using `useChatRoom` in more components, you might want to let components customize its behavior. For example, currently, the logic for what to do when a message arrives is hardcoded inside the Hook: +Lorsque vous commencez à utiliser `useChatRoom` dans un plus grand nombre de composants, vous souhaiteriez peut-être que ces derniers puissent personnaliser son comportement. Par exemple, actuellement, la logique de ce qu’il faut faire quand un message arrive est codée en dur à l’intérieur du Hook : ```js {9-11} export function useChatRoom({ serverUrl, roomId }) { @@ -855,14 +856,14 @@ export function useChatRoom({ serverUrl, roomId }) { const connection = createConnection(options); connection.connect(); connection.on('message', (msg) => { - showNotification('New message: ' + msg); + showNotification('Nouveaumessage : ' + msg); }); return () => connection.disconnect(); }, [roomId, serverUrl]); } ``` -Let's say you want to move this logic back to your component: +Disons que vous voulez déplacer cette logique à nouveau dans votre composant : ```js {7-9} export default function ChatRoom({ roomId }) { @@ -872,13 +873,13 @@ export default function ChatRoom({ roomId }) { roomId: roomId, serverUrl: serverUrl, onReceiveMessage(msg) { - showNotification('New message: ' + msg); + showNotification('Nouveau message : ' + msg); } }); // ... ``` -To make this work, change your custom Hook to take `onReceiveMessage` as one of its named options: +Pour que cela fonctionne, modifiez votre Hook personnalisé pour qu’il prenne `onReceiveMessage` comme l’une de ses options : ```js {1,10,13} export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { @@ -893,13 +894,13 @@ export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { onReceiveMessage(msg); }); return () => connection.disconnect(); - }, [roomId, serverUrl, onReceiveMessage]); // ✅ All dependencies declared + }, [roomId, serverUrl, onReceiveMessage]); // ✅ Toutes les dépendances sont déclarées. } ``` -This will work, but there's one more improvement you can do when your custom Hook accepts event handlers. +Cela fonctionnera, mais il y a une autre amélioration que vous pouvez apporter quand votre Hook personnalisé accepte des gestionnaires d’événements. -Adding a dependency on `onReceiveMessage` is not ideal because it will cause the chat to re-connect every time the component re-renders. [Wrap this event handler into an Effect Event to remove it from the dependencies:](/learn/removing-effect-dependencies#wrapping-an-event-handler-from-the-props) +Ajouter une dépendance à `onReceiveMessage` n’est pas idéal car il entraînera une reconnexion au salon à chaque réaffichage du composant. [Enrober ce gestionnaire d’état dans un événement d’effet pour le supprimer des dépendances :](/learn/removing-effect-dependencies#wrapping-an-event-handler-from-the-props) ```js {1,4,5,15,18} import { useEffect, useEffectEvent } from 'react'; @@ -919,11 +920,11 @@ export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { onMessage(msg); }); return () => connection.disconnect(); - }, [roomId, serverUrl]); // ✅ All dependencies declared + }, [roomId, serverUrl]); // ✅ Toutes les dépendances sont déclarées. } ``` -Now the chat won't re-connect every time that the `ChatRoom` component re-renders. Here is a fully working demo of passing an event handler to a custom Hook that you can play with: +Maintenant, le salon ne se reconnectera plus à chaque réaffichage du composant `ChatRoom`. Voici une démonstration complète du passage d’un gestionnaire d’événement à un Hook personnalisé avec laquelle vous pouvez jouer : @@ -941,9 +942,9 @@ export default function App() { value={roomId} onChange={e => setRoomId(e.target.value)} > - - - + + +
@@ -967,17 +968,17 @@ export default function ChatRoom({ roomId }) { roomId: roomId, serverUrl: serverUrl, onReceiveMessage(msg) { - showNotification('New message: ' + msg); + showNotification('Nouveau message: ' + msg); } }); return ( <> -

Welcome to the {roomId} room!

+

Bienvenue dans le salon {roomId} !

); } @@ -1008,18 +1009,18 @@ export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { ```js chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Une implémentation réelle se connecterait vraiment au serveur. if (typeof serverUrl !== 'string') { - throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); + throw Error('serverUrl doit être une chaîne de caractères. Reçu : ' + serverUrl); } if (typeof roomId !== 'string') { - throw Error('Expected roomId to be a string. Received: ' + roomId); + throw Error('roomId doit être une chaîne de caractères. Reçu : ' + roomId); } let intervalId; let messageCallback; return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Connexion au salon "' + roomId + '" depuis ' + serverUrl + '...'); clearInterval(intervalId); intervalId = setInterval(() => { if (messageCallback) { @@ -1034,14 +1035,14 @@ export function createConnection({ serverUrl, roomId }) { disconnect() { clearInterval(intervalId); messageCallback = null; - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl + ''); + console.log('❌ Déconnexion du salon "' + roomId + '" depuis ' + serverUrl + ''); }, on(event, callback) { if (messageCallback) { - throw Error('Cannot add the handler twice.'); + throw Error('Il n’est pas possible d’ajouter un gestionnaire deux fois.'); } if (event !== 'message') { - throw Error('Only "message" event is supported.'); + throw Error('Seul l’événement "message" est accepté.'); } messageCallback = callback; }, @@ -1091,20 +1092,20 @@ button { margin-left: 10px; }
-Notice how you no longer need to know *how* `useChatRoom` works in order to use it. You could add it to any other component, pass any other options, and it would work the same way. That's the power of custom Hooks. +Remarquez que vous n’avez plus besoin de savoir *comment* `useChatRoom` fonctionne pour pouvoir l’utiliser. Vous pourriez l’ajouter à n’importe quel autre composant, lui passer n’importe quelles autres options, il fonctionnerait de la même manière. C’est la puissance des Hooks personnalisés. -## When to use custom Hooks {/*when-to-use-custom-hooks*/} +## Quand utiliser des Hooks personnalisés {/*when-to-use-custom-hooks*/} -You don't need to extract a custom Hook for every little duplicated bit of code. Some duplication is fine. For example, extracting a `useFormInput` Hook to wrap a single `useState` call like earlier is probably unnecessary. +Il n’est pas nécessaire d’extraire un Hook personnalisé pour chaque petit bout de code dupliqué. Certaines duplications sont acceptables. Par exemple, extraire un Hook `useFormInput` pour enrober un seul appel de `useState` comme précédemment est probablement inutile. -However, whenever you write an Effect, consider whether it would be clearer to also wrap it in a custom Hook. [You shouldn't need Effects very often,](/learn/you-might-not-need-an-effect) so if you're writing one, it means that you need to "step outside React" to synchronize with some external system or to do something that React doesn't have a built-in API for. Wrapping it into a custom Hook lets you precisely communicate your intent and how the data flows through it. +Cependant, à chaque fois que vous écrivez un effet, demandez-vous s’il ne serait pas plus clair de l’enrober également dans un Hook personnalisé. [Vous ne devriez pas avoir besoin d’effets si souvent,](/learn/you-might-not-need-an-effect) alors si vous en écrivez un, cela signifie que vous devez ???step outside??? « sortir » de React pour vous synchroniser avec un système externe ou pour faire quelque chose pour lequel React n’a pas une API intégrée. L’enrober dans un Hook personnalisé vous permet de communiquer précisément votre intention et la manière dont les flux de données circulent à travers lui. -For example, consider a `ShippingForm` component that displays two dropdowns: one shows the list of cities, and another shows the list of areas in the selected city. You might start with some code that looks like this: +Prenons l’exemple d’un composant `ShippingForm` qui affiche deux listes déroulantes : l’une présente la liste des villes, l’autre affiche la liste des quartiers de la ville choisie. Vous pourriez démarrer avec un code ressemblant à ceci : ```js {3-16,20-35} function ShippingForm({ country }) { const [cities, setCities] = useState(null); - // This Effect fetches cities for a country + // Cet effet récupère les villes d’un pays. useEffect(() => { let ignore = false; fetch(`/api/cities?country=${country}`) @@ -1121,7 +1122,7 @@ function ShippingForm({ country }) { const [city, setCity] = useState(null); const [areas, setAreas] = useState(null); - // This Effect fetches areas for the selected city + // Cet effet récupère les quartiers de la ville choisie. useEffect(() => { if (city) { let ignore = false; @@ -1141,7 +1142,7 @@ function ShippingForm({ country }) { // ... ``` -Although this code is quite repetitive, [it's correct to keep these Effects separate from each other.](/learn/removing-effect-dependencies#is-your-effect-doing-several-unrelated-things) They synchronize two different things, so you shouldn't merge them into one Effect. Instead, you can simplify the `ShippingForm` component above by extracting the common logic between them into your own `useData` Hook: +Bien que ce code soit assez répétitif, [il est acceptable de garder ces effets séparés les uns des autres.](/learn/removing-effect-dependencies#is-your-effect-doing-several-unrelated-things) Ils synchronisent deux choses différentes, vous ne devez donc pas les fusionner en un seul effet. À la place, vous pouvez simplifier le composant `ShippingForm` ci-dessus en sortant la logique commune entre eux dans votre propre Hook `useData` : ```js {2-18} function useData(url) { @@ -1165,7 +1166,7 @@ function useData(url) { } ``` -Now you can replace both Effects in the `ShippingForm` components with calls to `useData`: +Vous pouvez maintenant remplacer les deux effets du composant `ShippingForm` par des appels à `useData` : ```js {2,4} function ShippingForm({ country }) { @@ -1175,39 +1176,39 @@ function ShippingForm({ country }) { // ... ``` -Extracting a custom Hook makes the data flow explicit. You feed the `url` in and you get the `data` out. By "hiding" your Effect inside `useData`, you also prevent someone working on the `ShippingForm` component from adding [unnecessary dependencies](/learn/removing-effect-dependencies) to it. With time, most of your app's Effects will be in custom Hooks. +Extraire un Hook personnalisé rend le flux des données explicite. Vous renseignez l’`url`, et vous obtenez le `data` en retour. En « cachant » votre effet dans `useData`, vous empêchez également que toute personne travaillant sur le composant `ShippingForm` d’y ajouter [des dépendances inutiles](/learn/removing-effect-dependencies). Avec le temps, la plupart des effets de votre app se trouveront dans des Hooks personnalisés. -#### Keep your custom Hooks focused on concrete high-level use cases {/*keep-your-custom-hooks-focused-on-concrete-high-level-use-cases*/} +#### Gardez vos Hooks personnalisés centrés sur des cas d’utilisation de haut niveau {/*keep-your-custom-hooks-focused-on-concrete-high-level-use-cases*/} -Start by choosing your custom Hook's name. If you struggle to pick a clear name, it might mean that your Effect is too coupled to the rest of your component's logic, and is not yet ready to be extracted. +Commencez par choisir le nom de votre Hook personnalisé. Si vous avez du mal à choisir un nom clair, cela peut signifier que votre effet est trop lié au reste de la logique de votre composant, et qu’il n’est pas encore prêt à être extrait. -Ideally, your custom Hook's name should be clear enough that even a person who doesn't write code often could have a good guess about what your custom Hook does, what it takes, and what it returns: +Dans l’idéal, le nom de votre Hook personnalisé doit être suffisament clair pour qu’une personne qui n’écrit pas souvent du code puisse deviner ce que fait votre Hook personnalisé, ce qu’il prend et ce qu’il renvoie : * ✅ `useData(url)` * ✅ `useImpressionLog(eventName, extraData)` * ✅ `useChatRoom(options)` -When you synchronize with an external system, your custom Hook name may be more technical and use jargon specific to that system. It's good as long as it would be clear to a person familiar with that system: +Lorsque vous vous synchronisez avec un système externe, le nom de votre Hook personnalisé peut être plus technique et utiliser un jargon spécifique à ce système. C’est une bonne chose tant que cela reste clair pour une personne familière avec ce système : * ✅ `useMediaQuery(query)` * ✅ `useSocket(url)` * ✅ `useIntersectionObserver(ref, options)` -**Keep custom Hooks focused on concrete high-level use cases.** Avoid creating and using custom "lifecycle" Hooks that act as alternatives and convenience wrappers for the `useEffect` API itself: +**Les Hooks personnalisés doivent restés focalisés sur des cas d’utilisation concrets de haut niveau.** Évitez de créer et d’utiliser de Hooks personnalisé de « cycle de vie » qui agissent comme des alternatives et des enrobage de commodité pour l’API `useEffect` elle-même : * 🔴 `useMount(fn)` * 🔴 `useEffectOnce(fn)` * 🔴 `useUpdateEffect(fn)` -For example, this `useMount` Hook tries to ensure some code only runs "on mount": +Par exemple, ce Hook `useMount` essaie de s’assurer que du code ne s’exécute qu’au « montage » : ```js {4-5,14-15} function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); - // 🔴 Avoid: using custom "lifecycle" Hooks + // 🔴 À éviter : utiliser des Hooks personnalisés de « cycle de vie ». useMount(() => { const connection = createConnection({ roomId, serverUrl }); connection.connect(); @@ -1217,23 +1218,23 @@ function ChatRoom({ roomId }) { // ... } -// 🔴 Avoid: creating custom "lifecycle" Hooks +// 🔴 À éviter : créer des Hooks personnalisés de « cycle de vie ». function useMount(fn) { useEffect(() => { fn(); - }, []); // 🔴 React Hook useEffect has a missing dependency: 'fn' + }, []); // 🔴 Le Hook React useEffect a une dépendance manquante : 'fn' } ``` -**Custom "lifecycle" Hooks like `useMount` don't fit well into the React paradigm.** For example, this code example has a mistake (it doesn't "react" to `roomId` or `serverUrl` changes), but the linter won't warn you about it because the linter only checks direct `useEffect` calls. It won't know about your Hook. +**Les Hooks personnalisés de « cycle de vie » comme `useMount` ne s’intègrent pas bien dans le paradigme de React.** Par exemple, ce code contient une erreur (il ne « réagit » pas aux changements de `roomId` ou `serverUrl` changes), mais le linter ne vous avertira pas à ce sujet car le linter ne vérifie que les appels directs à `useEffect`. Il ne connaîtra rien de votre Hook. -If you're writing an Effect, start by using the React API directly: +Si vous écrivez un effet, commencez par utiliser directement une API de React : ```js function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); - // ✅ Good: two raw Effects separated by purpose + // ✅ Correct : deux effets bruts séparés par leur finalité. useEffect(() => { const connection = createConnection({ serverUrl, roomId }); @@ -1249,28 +1250,28 @@ function ChatRoom({ roomId }) { } ``` -Then, you can (but don't have to) extract custom Hooks for different high-level use cases: +Ensuite, vous pouvez (mais ce n’est pas obligatoire) extraire des Hooks personnalisés pour différents cas d’utilisation de haut niveau : ```js function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); - // ✅ Great: custom Hooks named after their purpose + // ✅ Excellent : des Hooks personnalisés nommés selon leur fonction. useChatRoom({ serverUrl, roomId }); useImpressionLog('visit_chat', { roomId }); // ... } ``` -**A good custom Hook makes the calling code more declarative by constraining what it does.** For example, `useChatRoom(options)` can only connect to the chat room, while `useImpressionLog(eventName, extraData)` can only send an impression log to the analytics. If your custom Hook API doesn't constrain the use cases and is very abstract, in the long run it's likely to introduce more problems than it solves. +**Un bon Hook personnalisé rend le code appelé plus déclaratif en limitant ce qu’il fait.** Par exemple, `useChatRoom(options)` ne peut que se connecter à un salon de discussion, tandis que `useImpressionLog(eventName, extraData)` ne peut qu’envoyer les journaux à un système d’analytique. Si l’API de votre Hook personnalisé ne limite pas les cas d’utilisation et est très abstraite, elle risque d'introduire à long terme plus de problème qu’elle n’en résoudra. -### Custom Hooks help you migrate to better patterns {/*custom-hooks-help-you-migrate-to-better-patterns*/} +### Les Hooks personnalisés vous aident à migrer vers de meilleurs modèles {/*custom-hooks-help-you-migrate-to-better-patterns*/} -Effects are an ["escape hatch"](/learn/escape-hatches): you use them when you need to "step outside React" and when there is no better built-in solution for your use case. With time, the React team's goal is to reduce the number of the Effects in your app to the minimum by providing more specific solutions to more specific problems. Wrapping your Effects in custom Hooks makes it easier to upgrade your code when these solutions become available. +Les effets sont un [« échappatoire »](/learn/escape-hatches) : vous les utiliser quand vous avez besoin de « sortir » de React et quand il n’y a pas de meilleure solution intégrée pour votre cas d’utilisation. Avec le temps, le but de l’équipe de React est de limiter au minimum le nombre d’effets dans votre app en fournissant des solutions plus spécifiques à des problèmes plus spécifiques. Enrober vos effets dans des Hooks personnalisés facilite la mise à jour de votre code lorsque ces solutions deviennent disponibles. -Let's return to this example: +Revenons à cet exemple : @@ -1279,19 +1280,19 @@ import { useOnlineStatus } from './useOnlineStatus.js'; function StatusBar() { const isOnline = useOnlineStatus(); - return

{isOnline ? '✅ Online' : '❌ Disconnected'}

; + return

{isOnline ? '✅ Connecté' : '❌ Déconnecté'}

; } function SaveButton() { const isOnline = useOnlineStatus(); function handleSaveClick() { - console.log('✅ Progress saved'); + console.log('✅ Progression enregistrée'); } return ( ); } @@ -1331,9 +1332,9 @@ export function useOnlineStatus() {
-In the above example, `useOnlineStatus` is implemented with a pair of [`useState`](/reference/react/useState) and [`useEffect`.](/reference/react/useEffect) However, this isn't the best possible solution. There is a number of edge cases it doesn't consider. For example, it assumes that when the component mounts, `isOnline` is already `true`, but this may be wrong if the network already went offline. You can use the browser [`navigator.onLine`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine) API to check for that, but using it directly would not work on the server for generating the initial HTML. In short, this code could be improved. +Dans l’exemple ci-dessus, `useOnlineStatus` est implémentée avec une paire de [`useState`](/reference/react/useState) et [`useEffect`.](/reference/react/useEffect) Cependant, ce n’est pas la meilleure solution possible. Elle ne tient pas compte d’un certain nombre de cas limites. Par exemple, elle suppose que lorsque le composant est monté, `isOnline` est déjà à `true`, mais cela peut être faux si le réseau a déjà été mis hors ligne. Vous pouvez utiliser l’API du navigateur [`navigator.onLine`](https://developer.mozilla.org/fr/docs/Web/API/Navigator/onLine) pour vérifier cela, mais l’utiliser directement ne marchera pas sur le serveur pour générer le HTML initial. En bref, ce code peut être amélioré. -Luckily, React 18 includes a dedicated API called [`useSyncExternalStore`](/reference/react/useSyncExternalStore) which takes care of all of these problems for you. Here is how your `useOnlineStatus` Hook, rewritten to take advantage of this new API: +Heureusement, React 18 inclut une API dédiée appelée [`useSyncExternalStore`](/reference/react/useSyncExternalStore) qui se charge de tous ces problèmes pour vous. Voici comment votre Hook personnalisé `useOnlineStatus` est réécrit pour tirer avantage de cette nouvelle API : @@ -1342,19 +1343,19 @@ import { useOnlineStatus } from './useOnlineStatus.js'; function StatusBar() { const isOnline = useOnlineStatus(); - return

{isOnline ? '✅ Online' : '❌ Disconnected'}

; + return

{isOnline ? '✅ Connecté' : '❌ Déconnecté'}

; } function SaveButton() { const isOnline = useOnlineStatus(); function handleSaveClick() { - console.log('✅ Progress saved'); + console.log('✅ Progression enregistrée'); } return ( ); } @@ -1384,8 +1385,8 @@ function subscribe(callback) { export function useOnlineStatus() { return useSyncExternalStore( subscribe, - () => navigator.onLine, // How to get the value on the client - () => true // How to get the value on the server + () => navigator.onLine, // Comment récupérer la valeur sur le client. + () => true // Comment récupérer la valeur sur le serveur. ); } @@ -1393,7 +1394,7 @@ export function useOnlineStatus() {
-Notice how **you didn't need to change any of the components** to make this migration: +Remarquez comment vous **n’avez pas eu besoin de modifier les composants** pour faire cette migration : ```js {2,7} function StatusBar() { @@ -1407,22 +1408,22 @@ function SaveButton() { } ``` -This is another reason for why wrapping Effects in custom Hooks is often beneficial: +C’est une raison pour laquelle il est souvent utile d’enrober des effets dans des Hooks personnalisés : -1. You make the data flow to and from your Effects very explicit. -2. You let your components focus on the intent rather than on the exact implementation of your Effects. -3. When React adds new features, you can remove those Effects without changing any of your components. +1. Vous rendez le flux de données vers et depuis vos effets très explicite. +2. Vous permettez à vos composants de se concentrer sur l’intention plutôt que sur l’implémentation exacte de vos effets. +3. Lorsque React ajoute de nouvelles fonctionnalités, vous pouvez retirer ces effets sans changer aucun de vos composants. -Similar to a [design system,](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969) you might find it helpful to start extracting common idioms from your app's components into custom Hooks. This will keep your components' code focused on the intent, and let you avoid writing raw Effects very often. Many excellent custom Hooks are maintained by the React community. +À la manière d’un [système de design,](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969) vous pourriez trouver utile de commencer à extraire les idiomes communs des composants de votre app dans des Hooks personnalisés. Cela le code de vos composants restera centré sur l’intention et vous éviterez la plupart du temps d’utiliser des effets bruts. De nombreux Hooks personnalisés de qualité sont maintenus par la communauté de React. -#### Will React provide any built-in solution for data fetching? {/*will-react-provide-any-built-in-solution-for-data-fetching*/} +#### React fournira-t-il une solution intégrée pour la récupération des données ? {/*will-react-provide-any-built-in-solution-for-data-fetching*/} -We're still working out the details, but we expect that in the future, you'll write data fetching like this: +Nous travaillons encore sur les détails, mais nous pensons qu’à l’avenir, vous écrirez la récupération de données de cette façon : ```js {1,4,6} -import { use } from 'react'; // Not available yet! +import { use } from 'react'; // Pas encore disponible ! function ShippingForm({ country }) { const cities = use(fetch(`/api/cities?country=${country}`)); @@ -1431,13 +1432,13 @@ function ShippingForm({ country }) { // ... ``` -If you use custom Hooks like `useData` above in your app, it will require fewer changes to migrate to the eventually recommended approach than if you write raw Effects in every component manually. However, the old approach will still work fine, so if you feel happy writing raw Effects, you can continue to do that. +Si vous utilisez des Hooks personnalisés comme le `useData` plus haut dans votre app, la migration vers l’approche éventuellement recommandée nécessitera moins de changements que si vous écrivez manuellement des effets bruts dans chaque composant. Cependant, l’ancienne approche continuera de bien fonctionner, donc si vous vous sentez à l’aise en écrivant des effets bruts, vous pouvez continuer ainsi. -### There is more than one way to do it {/*there-is-more-than-one-way-to-do-it*/} +### Il y a plus d’une façon de faire {/*there-is-more-than-one-way-to-do-it*/} -Let's say you want to implement a fade-in animation *from scratch* using the browser [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) API. You might start with an Effect that sets up an animation loop. During each frame of the animation, you could change the opacity of the DOM node you [hold in a ref](/learn/manipulating-the-dom-with-refs) until it reaches `1`. Your code might start like this: +Supposons que vous voulez implémenter une animation de fondu-enchaîné *en partant de zéro* en utilisant l’API du navigateur [`requestAnimationFrame`](https://developer.mozilla.org/fr/docs/Web/API/window/requestAnimationFrame). Vous pouvez commencer par un effet qui initialise une boucle d’animation. Pendant chaque image de l’animation, vous pourriez changer l’opacité du nœud du DOM si vous le [conservez dans une ref](/learn/manipulating-the-dom-with-refs) jusqu’à ce qu’il atteigne `1`. Votre code pourrait commencer ainsi : @@ -1459,7 +1460,7 @@ function Welcome() { const progress = Math.min(timePassed / duration, 1); onProgress(progress); if (progress < 1) { - // We still have more frames to paint + // Nous avons encore des frames à dessiner. frameId = requestAnimationFrame(onFrame); } } @@ -1486,7 +1487,7 @@ function Welcome() { return (

- Welcome + Bienvenue

); } @@ -1496,7 +1497,7 @@ export default function App() { return ( <>
{show && } @@ -1520,7 +1521,7 @@ html, body { min-height: 300px; }
-To make the component more readable, you might extract the logic into a `useFadeIn` custom Hook: +Pour rendre le composant plus lisible, vous pouvez extraire la logique dans un Hook personnalisé `useFadeIn` : @@ -1535,7 +1536,7 @@ function Welcome() { return (

- Welcome + Bienvenue

); } @@ -1545,7 +1546,7 @@ export default function App() { return ( <>
{show && } @@ -1569,7 +1570,7 @@ export function useFadeIn(ref, duration) { const progress = Math.min(timePassed / duration, 1); onProgress(progress); if (progress < 1) { - // We still have more frames to paint + // Nous avons encore des frames à dessiner. frameId = requestAnimationFrame(onFrame); } } @@ -1611,7 +1612,7 @@ html, body { min-height: 300px; }
-You could keep the `useFadeIn` code as is, but you could also refactor it more. For example, you could extract the logic for setting up the animation loop out of `useFadeIn` into a custom `useAnimationLoop` Hook: +Vous pouvez conserver le code de `useFadeIn` ainsi, mais vous pouvez le remanier encore. Par exemple, vous pourriez extraire la logique de mise en place de la boucle d’animation de `useFadeIn` dans un Hook personnalisé `useAnimationLoop` : @@ -1626,7 +1627,7 @@ function Welcome() { return (

- Welcome + Bienvenue

); } @@ -1636,7 +1637,7 @@ export default function App() { return ( <>
{show && } @@ -1715,7 +1716,7 @@ html, body { min-height: 300px; }
-However, you didn't *have to* do that. As with regular functions, ultimately you decide where to draw the boundaries between different parts of your code. You could also take a very different approach. Instead of keeping the logic in the Effect, you could move most of the imperative logic inside a JavaScript [class:](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) +Cependant, vous n’avez pas *besoin* de faire ça. Comme pour les fonctions ordinaires, c’est finalement à vous de définir les limites entre les différentes parties de votre code. Vous pouvez également adopter une approche tout à fait différente. Au lieu de conserver votre logique dans un effet, vous pouvez déplacer la plupart de la logique impérative dans une [classe](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Classes) JavaScript : @@ -1730,7 +1731,7 @@ function Welcome() { return (

- Welcome + Bienvenue

); } @@ -1740,7 +1741,7 @@ export default function App() { return ( <>
{show && } @@ -1782,7 +1783,7 @@ export class FadeInAnimation { if (progress === 1) { this.stop(); } else { - // We still have more frames to paint + // Nous avons encore des frames à dessiner. this.frameId = requestAnimationFrame(() => this.onFrame()); } } @@ -1813,9 +1814,9 @@ html, body { min-height: 300px; }
-Effects let you connect React to external systems. The more coordination between Effects is needed (for example, to chain multiple animations), the more it makes sense to extract that logic out of Effects and Hooks *completely* like in the sandbox above. Then, the code you extracted *becomes* the "external system". This lets your Effects stay simple because they only need to send messages to the system you've moved outside React. +Les effets permettent à React de se connecter à des systèmes externes. Plus la coordination entre les effets est nécessaire (par exemple pour enchaîner des animations multiples), plus il est sensé d’extraire *complètement* cette logique des effets et des Hooks, comme dans le bac à sable ci-dessus. Le code extrait *devient* ainsi le « système externe ». Cela permet à vos effets de rester simple car ils n’auront qu’à envoyer des messages au système que vous avez sorti de React. -The examples above assume that the fade-in logic needs to be written in JavaScript. However, this particular fade-in animation is both simpler and much more efficient to implement with a plain [CSS Animation:](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations) +Les exemples ci-dessus supposent que la logique de fondu-enchaîné soit écrite en JavaScript. Cependant, cette animation particulière de fondu-enchaîné est à la fois plus simple et beaucoup plus efficace lorsqu’elle est écrite par une simple [animation CSS :](https://developer.mozilla.org/fr/docs/Web/CSS/CSS_Animations/Using_CSS_animations) @@ -1826,7 +1827,7 @@ import './welcome.css'; function Welcome() { return (

- Welcome + Bienvenue

); } @@ -1836,7 +1837,7 @@ export default function App() { return ( <>
{show && } @@ -1870,19 +1871,19 @@ html, body { min-height: 300px; }
-Sometimes, you don't even need a Hook! +Parfois, vous n’avez même pas besoin d’un Hook ! -- Custom Hooks let you share logic between components. -- Custom Hooks must be named starting with `use` followed by a capital letter. -- Custom Hooks only share stateful logic, not state itself. -- You can pass reactive values from one Hook to another, and they stay up-to-date. -- All Hooks re-run every time your component re-renders. -- The code of your custom Hooks should be pure, like your component's code. -- Wrap event handlers received by custom Hooks into Effect Events. -- Don't create custom Hooks like `useMount`. Keep their purpose specific. -- It's up to you how and where to choose the boundaries of your code. +- Les Hooks personnalisés vous permettent de partager la logique entre les composants. +- Le nom des Hooks personnalisés doit commencer par `use` et être suivi d’une lettre majuscule. +- Les Hooks personnalisés ne partagent que la logique d’état et non l’état lui-même. +- Vous pouvez passer des valeurs réactives d’un Hook à un autre, et elles restent à jour. +- Tous les Hooks sont réexécutés à chaque réaffichage de votre composant. +- Le code de vos Hooks personnalisés doit être pur, comme le code de votre composant. +- Enrobez les gestionnaires d’événements reçus par les Hooks personnalisés dans des événéments d’effet. +- Ne créez pas des Hooks personnalisés comme `useMount`. Veillez à ce que leur objectif soit spécifique. +- C’est à vous de décider comment et où choisir les limites de votre code. From 86eff3ee25e0bc80631a8cf08eece3b4358ee761 Mon Sep 17 00:00:00 2001 From: linsolas Date: Thu, 4 May 2023 12:17:33 +0200 Subject: [PATCH 2/5] doc(custom-hook): end of translation --- .../learn/reusing-logic-with-custom-hooks.md | 194 +++++++++--------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/src/content/learn/reusing-logic-with-custom-hooks.md b/src/content/learn/reusing-logic-with-custom-hooks.md index 89445367f..2ab7c457f 100644 --- a/src/content/learn/reusing-logic-with-custom-hooks.md +++ b/src/content/learn/reusing-logic-with-custom-hooks.md @@ -10,7 +10,7 @@ React intègre plusieurs Hooks tels que `useState`, `useContext` et `useEffect`. -- Que sont les Hooks personnalisés et comment écrire les vôtres +- Ce que sont les Hooks personnalisés et comment écrire les vôtres - Comment réutiliser la logique entre composants - Comment nommer et structurer vos Hooks personnalisés - Quand et comment extraire des Hooks personnalisés @@ -19,12 +19,12 @@ React intègre plusieurs Hooks tels que `useState`, `useContext` et `useEffect`. ## Hooks personnalisés : partager la logique entre composants {/*custom-hooks-sharing-logic-between-components*/} -Imaginez que vous développez une appli qui repose massivement sur le réseau (comme la plupart des applis le font). Vous souhaitez avertir l’utilisateur si sa connexion au réseau s’est brutalement interrompue pendant qu’il utilisait son appli. Comment feriez-vous cela ? Il semble que vous ayez besoin de deux choses dans votre composant : +Imaginez que vous développez une appli qui repose massivement sur le réseau (comme la plupart des applis le font). Vous souhaitez avertir l’utilisateur si sa connexion au réseau s’est brutalement interrompue pendant qu’il utilisait son appli. Comment feriez-vous ça ? Il semble que vous ayez besoin de deux choses dans votre composant : 1. Un élément d’état qui détermine si le réseau est en ligne ou non. 2. Un effet qui s’abonne aux événements globaux [`online`](https://developer.mozilla.org/fr/docs/Web/API/Window/online_event) et [`offline`](https://developer.mozilla.org/fr/docs/Web/API/Window/offline_event), et met à jour cet état. -Cela permettra à votre composant de rester [synchronisé](/learn/synchronizing-with-effects) avec l’état du réseau. Vous pouvez commencer par quelque chose comme ceci : +Ça permettra à votre composant de rester [synchronisé](/learn/synchronizing-with-effects) avec l’état du réseau. Vous pouvez commencer par quelque chose comme ceci : @@ -56,7 +56,7 @@ export default function StatusBar() { Essayez d’activer et de désactiver votre réseau et remarquez comment cette `StatusBar` se met à jour en fonction de vos actions. -Imaginez maintenant que vous souhaitiez utiliser la *même* logique dans un composant différent. Vous souhaitez créer un bouton Enregistrer qui sera désactivé et affichera « Reconnexion… » au lieu de « Enregistrer » lorsque le réseau est désactivé. +Imaginez maintenant que vous souhaitiez utiliser la *même* logique dans un composant différent. Vous voulez créer un bouton Enregistrer qui sera désactivé et affichera « Reconnexion… » au lieu de « Enregistrer » lorsque le réseau est désactivé. Pour commencer, vous pouvez copier et coller l’état `isOnline` et l’effet dans le `SaveButton` : @@ -96,7 +96,7 @@ export default function SaveButton() { -Vérifiez que, si vous éteignez le réseau, le bouton changera d’apparence. +Vérifiez que le bouton changera d’apparence si vous éteignez le réseau. Ces deux composants fonctionnent bien, mais la duplication de la logique entre eux est regrettable. Il semble que s’ils ont un *aspect visuel* différent, ils réutilisent la même logique. @@ -125,7 +125,7 @@ function SaveButton() { } ``` -Bien qu'il n'y ait pas de tel Hook intégré, vous pouvez l’écrire vous-même. Déclarez une fonction appelée `useOnlineStatus` et déplacez-y tout le code dupliqué des composants que vous avez écrits plus tôt : +Même s’il n’existe pas un tel Hook intégré, vous pouvez l’écrire vous-même. Déclarez une fonction appelée `useOnlineStatus` et déplacez-y tout le code dupliqué des composants que vous avez écrits plus tôt : ```js {2-16} function useOnlineStatus() { @@ -148,7 +148,7 @@ function useOnlineStatus() { } ``` -À la fin de la fonction, retournez `isOnline`. Cela permet à votre composant de lire cette valeur : +À la fin de la fonction, retournez `isOnline`. Ça permet à votre composant de lire cette valeur : @@ -211,24 +211,24 @@ export function useOnlineStatus() { Vérifiez que l’activation et la désactivation du réseau met à jour les deux composants. -Désormais, Vos composants n’ont plus de logique répétitive. **Plus important encore, le code qu'ils contiennent décrivent *ce qu'ils veulent faire* (utiliser le statut de connexion) plut^pt que *la manière de le faire* (en s’abonnant aux événements du navigateur).** +Désormais, Vos composants n’ont plus de logique répétitive. **Plus important encore, le code qu’ils contiennent décrivent *ce qu’ils veulent faire* (utiliser le statut de connexion) plutôt que *la manière de le faire* (en s’abonnant aux événements du navigateur).** -Quand vous extrayez la logique dans des Hooks personnalisés, vous pouvez cacher les détails de la façon dont vous traitez avec des systèmes externes ou d’une API du navigateur. Le code de vos composants expriment votre intention et pas l’implémentation. +Quand vous extrayez la logique dans des Hooks personnalisés, vous pouvez cacher les détails de la façon dont vous traitez avec des systèmes externes ou avec une API du navigateur. Le code de vos composants expriment votre intention, pas l’implémentation. ### Les noms des Hooks commencent toujours par `use` {/*hook-names-always-start-with-use*/} -Les applications React sont constuites à partir de composants. Les composants sont construits à partir des Hooks, qu'ils soient intégrés ou personnalisés. Vous utiliserez probablement souvent des Hooks personnalisés créés par d'autres, mais vous pourrez occasionnellement en écrire un vous-même ! +Les applications React sont constuites à partir de composants. Les composants sont construits à partir des Hooks, qu’ils soient intégrés ou personnalisés. Vous utiliserez probablement souvent des Hooks personnalisés créés par d’autres, mais vous pourrez occasionnellement en écrire un vous-même ! Vous devez respecter les conventions de nommage suivantes : 1. **Le nom des composants React doit commencer par une lettre en majuscule,** comme `StatusBar` et `SaveButton`. Les composants React doivent également renvoyer quelque chose que React sait afficher, comme un morceau de JSX. 2. **Le nom des Hook doit commencer par `use` suivi d’une majuscule,** comme [`useState`](/reference/react/useState) (intégré) ou `useOnlineStatus` (personnalisé, comme plus haut dans la page). Les Hooks peuvent renvoyer des valeurs arbitraires. -Cette convention garantit que vous pouvez toujours regarder un composant et savoir où son état, ses effets et d’autres fonctionnalités de React peuvent se « cacher ». Par exemple, si vous voyez un appel à la fonction `getColor()` dans votre composant, vous pouvez être sûr qu’il ne contient pas d’état React car son nom ne commence par par `use`. Cependant, un appel de fonction comme `useOnlineStatus()` contiendra très probablement des appels à d’autres Hooks à l’intérieur. +Cette convention garantit que vous pouvez toujours regarder un composant et savoir où son état, ses effets et d’autres fonctionnalités de React peuvent se « cacher ». Par exemple, si vous voyez un appel à la fonction `getColor()` dans votre composant, vous pouvez être sûr qu’il ne contient pas d’état React car son nom ne commence pas par `use`. Cependant, un appel de fonction comme `useOnlineStatus()` contiendra très probablement des appels à d’autres Hooks à l’intérieur. -Si votre linter est [configuré pour React,](/learn/editor-setup#linting) il appliquera cette convention de nommage. Remontez jusqu’au bac à sable et renommez `useOnlineStatus` en `getOnlineStatus`. Notez que le linter ne vous permettra plus appeler `useState` ou `useEffect` à l’intérieur. Seuls les Hooks et les composants peuvent appeler d’autres Hooks ! +Si votre linter est [configuré pour React,](/learn/editor-setup#linting) il appliquera cette convention de nommage. Remontez jusqu’au bac à sable et renommez `useOnlineStatus` en `getOnlineStatus`. Remarquez que le linter ne vous permettra plus appeler `useState` ou `useEffect` à l’intérieur. Seuls les Hooks et les composants peuvent appeler d’autres Hooks ! @@ -236,9 +236,9 @@ Si votre linter est [configuré pour React,](/learn/editor-setup#linting) il app #### Toutes les fonctions appelées pendant le rendu doivent-elles commencer par le préfixe use ? {/*should-all-functions-called-during-rendering-start-with-the-use-prefix*/} -Non. Les fonctions qui n’*appelent* pas des Hooks n’ont pas besoin d’*être* des Hooks. +Non. Les fonctions qui n’*appellent* pas des Hooks n’ont pas besoin d’*être* des Hooks. -Si votre fonction n’appellent aucun Hook, évitez d’utiliser le préfixe `use`. À la place, écrivez une fonction normale *sans* le préfixe `use`. Par exemple, `useSorted` ci-dessous n’appelle pas de Hook, appelez-la `getSorted` à la place : +Si votre fonction n’appelle aucun Hook, évitez d’utiliser le préfixe `use`. À la place, écrivez une fonction normale *sans* le préfixe `use`. Par exemple, `useSorted` ci-dessous n’appelle pas de Hook, appelez-la `getSorted` à la place : ```js // 🔴 À éviter : un Hook qui n’utilise pas d’autre Hooks. @@ -252,7 +252,7 @@ function getSorted(items) { } ``` -Cela garantit que votre code peut appeler cette fonction normale n’importe où, y compris dans ces conditions : +Ça garantit que votre code peut appeler cette fonction normale n’importe où, y compris dans ces conditions : ```js function List({ items, shouldSort }) { @@ -274,7 +274,7 @@ function useAuth() { } ``` -Techniquement, cette règle n’est pas dictée par React. En principe, vous pouvez créer un Hook qui n’appelle pas d’autres Hooks. C’est souvent déroutant et limitant, aussi il est préférable d’éviter ce modèle. Cependant, il peut y avoir de rares cas où cela est utile. Par exemple, votre fonction n’appelle pas encore de Hook pour l’instant, mais vous prévoyez d’y ajouter des appels de Hooks dans le futur. Il est alors logique d’utiliser le préfixe `use` : +Techniquement, cette règle n’est pas dictée par React. En principe, vous pouvez créer un Hook qui n’appelle pas d’autres Hooks. C’est souvent déroutant et limitant, aussi est-il préférable d’éviter ce modèle. Cependant, il peut y avoir de rares cas où c’est utile. Par exemple, votre fonction n’appelle pas encore de Hook pour l’instant, mais vous prévoyez d’y ajouter des appels à des Hooks dans le futur. Il est alors logique d’utiliser le préfixe `use` : ```js {3-4} @@ -286,7 +286,7 @@ function useAuth() { } ``` -Les composants ne pourront pas l’appeler de manière conditionnelle. Cela deviendra important quand vous ajouterez des appels à des Hooks à l’intérieur. Si vous ne prévoyez pas d’appeler des Hooks à l’intérieur (ni maintenant ni plus tard), alors n’en faites pas un Hook. +Les composants ne pourront pas l’appeler de manière conditionnelle. Ça deviendra important quand vous ajouterez des appels à des Hooks à l’intérieur. Si vous ne prévoyez pas d’appeler des Hooks à l’intérieur (ni maintenant ni plus tard), alors n’en faites pas un Hook. @@ -306,7 +306,7 @@ function SaveButton() { } ``` -Cela fonctionne de la même façon qu’avant la suppression de la duplication : +Ça fonctionne de la même façon qu’avant la suppression de la duplication : ```js {2-5,10-13} function StatusBar() { @@ -372,7 +372,7 @@ input { margin-left: 10px; } Il y a une logique répétitive pour chaque champ du formulaire : -1. Il y a un élément de l’état (`firstName` et `lastName`). +1. Il y a un élément d’état (`firstName` et `lastName`). 1. Il y a un gestionnaire de changement (`handleFirstNameChange` et `handleLastNameChange`). 1. Il y a un morceau de JSX qui spécifie les attributs `value` et `onChange` pour ce champ. @@ -440,7 +440,7 @@ function Form() { // ... ``` -C’est pourquoi cela revient à déclarer deux variables d’état distinctes ! +C’est pourquoi ça revient à déclarer deux variables d’état distinctes ! **Les Hooks personnalisés vous permettent de partager *la logique d’état* et non *l’état lui-même.* Chaque appel à un Hook est complètement indépendant de tous les autres appels au même Hook.** C’est pourquoi les deux bacs à sable ci-dessus sont totalement équivalents. Si vous le souhaitez, revenez en arrière et comparez-les. Le comportement avant et après l’extraction d’un Hook personnalisé est identique. @@ -450,7 +450,7 @@ Lorsque vous avez besoin de partager l’état lui-même entre plusieurs composa Le code contenu dans vos Hooks personnalisés sera réexécuté à chaque nouvel affichage de votre composant. C’est pourquoi, comme les composants, les Hooks personnalisés [doivent être purs.](/learn/keeping-components-pure) Considérez le code des Hooks personnalisés comme une partie du corps de votre composant ! -Comme les Hooks personnsalisés sont réaffichés en même temps que votre composant, ils reçoivent toujours les props et l’état les plus récents. Pour comprendre ce que cela signifie, prenez cet exemple de salon de discussion. Changez l’URL du serveur ou le salon de discussion : +Comme les Hooks personnsalisés sont réaffichés en même temps que votre composant, ils reçoivent toujours les props et l’état les plus récents. Pour comprendre ce que ça signifie, prenez cet exemple de salon de discussion. Changez l’URL du serveur ou le salon de discussion : @@ -600,7 +600,7 @@ button { margin-left: 10px; } -Quand vous changez `serverUrl` ou `roomId`, l’effet ["réagit" à vos changements](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) et se re-synchronise. Vous pouvez voir dans les messages de la console que le chat se reconnecte à chaque fois que vous changez les dépendances de votre effet. +Quand vous changez `serverUrl` ou `roomId`, l’effet [« réagit » à vos changements](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) et se re-synchronise. Vous pouvez voir dans les messages de la console que le chat se reconnecte à chaque fois que vous changez les dépendances de votre effet. Maintenant, déplacez le code de l’effet dans un Hook personnalisé : @@ -621,7 +621,7 @@ export function useChatRoom({ serverUrl, roomId }) { } ``` -Cela permet à votre composant `ChatRoom` d’appeler le Hook personnalisé sans se préoccuper de la façon dont il fonctionne à l’intérieur. +Ça permet à votre composant `ChatRoom` d’appeler le Hook personnalisé sans se préoccuper de la façon dont il fonctionne à l’intérieur. ```js {4-7} export default function ChatRoom({ roomId }) { @@ -644,7 +644,7 @@ export default function ChatRoom({ roomId }) { } ``` -Ceci semble bien plus simple ! (Mais fait toujours la même chose.) +C’est plus simple ainsi ! (Mais ça fait toujours la même chose.) Remarquez que la logique *répond toujours* aux changement des props et de l’état. Essayez de modifier l’URL du serveur ou le salon choisi : @@ -821,7 +821,7 @@ export default function ChatRoom({ roomId }) { // ... ``` -puis la transmettre à un autre Hook : +puis la transmettez à un autre Hook : ```js {6} export default function ChatRoom({ roomId }) { @@ -834,7 +834,7 @@ export default function ChatRoom({ roomId }) { // ... ``` -Chaque vois que votre composant `ChatRoom` est réaffiché, il passe les dernières valeurs de `roomId` et `serverUrl` à votre Hook. Ceci explique pourquoi votre effet se reconnecte au salon à cahque vois que leurs valeurs sont différentes après un nouveal affichage. (Si vous avez déjà travaillé avec des logiciels de traitement d’audio ou de vidéo, ce type d’enchaînement de Hooks peut vous rappeler l’enchaînement d’effets visuels ou sonores. C’est comme si le retour de `useState` « alimentait » l’entré de `useChatRoom`.) +Chaque vois que votre composant `ChatRoom` est réaffiché, il passe les dernières valeurs de `roomId` et `serverUrl` à votre Hook. Ceci explique pourquoi votre effet se reconnecte au salon à chaque fois que leurs valeurs sont différentes après un nouveal affichage. (Si vous avez déjà travaillé avec des logiciels de traitement d’audio ou de vidéo, ce type d’enchaînement de Hooks peut vous rappeler l’enchaînement d’effets visuels ou sonores. C’est comme si le retour de `useState` « alimentait » l’entrée de `useChatRoom`.) ### Transmettre des gestionnaires d’événements à des Hooks personnalisés {/*passing-event-handlers-to-custom-hooks*/} @@ -844,7 +844,7 @@ Cette section décrit une **API expérimentale qui n’a pas encore été livré -Lorsque vous commencez à utiliser `useChatRoom` dans un plus grand nombre de composants, vous souhaiteriez peut-être que ces derniers puissent personnaliser son comportement. Par exemple, actuellement, la logique de ce qu’il faut faire quand un message arrive est codée en dur à l’intérieur du Hook : +Lorsque vous commencez à utiliser `useChatRoom` dans un nombre plus important de composants, vous souhaitez peut-être que ces derniers puissent personnaliser son comportement. Par exemple, pour l’instant la logique de ce qu’il faut faire quand un message arrive est codée en dur à l’intérieur du Hook : ```js {9-11} export function useChatRoom({ serverUrl, roomId }) { @@ -856,7 +856,7 @@ export function useChatRoom({ serverUrl, roomId }) { const connection = createConnection(options); connection.connect(); connection.on('message', (msg) => { - showNotification('Nouveaumessage : ' + msg); + showNotification('Nouveau message : ' + msg); }); return () => connection.disconnect(); }, [roomId, serverUrl]); @@ -879,7 +879,7 @@ export default function ChatRoom({ roomId }) { // ... ``` -Pour que cela fonctionne, modifiez votre Hook personnalisé pour qu’il prenne `onReceiveMessage` comme l’une de ses options : +Pour que ça fonctionne, modifiez votre Hook personnalisé pour qu’il prenne `onReceiveMessage` comme l’une de ses options : ```js {1,10,13} export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { @@ -898,9 +898,9 @@ export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { } ``` -Cela fonctionnera, mais il y a une autre amélioration que vous pouvez apporter quand votre Hook personnalisé accepte des gestionnaires d’événements. +Ça fonctionnera, mais il y a une autre amélioration que vous pouvez apporter quand votre Hook personnalisé accepte des gestionnaires d’événements. -Ajouter une dépendance à `onReceiveMessage` n’est pas idéal car il entraînera une reconnexion au salon à chaque réaffichage du composant. [Enrober ce gestionnaire d’état dans un événement d’effet pour le supprimer des dépendances :](/learn/removing-effect-dependencies#wrapping-an-event-handler-from-the-props) +Ajouter une dépendance à `onReceiveMessage` n’est pas idéal car il entraînera une reconnexion au salon à chaque réaffichage du composant. [Enrobez ce gestionnaire d’état dans un événement d’effet pour le supprimer des dépendances :](/learn/removing-effect-dependencies#wrapping-an-event-handler-from-the-props) ```js {1,4,5,15,18} import { useEffect, useEffectEvent } from 'react'; @@ -1098,7 +1098,7 @@ Remarquez que vous n’avez plus besoin de savoir *comment* `useChatRoom` foncti Il n’est pas nécessaire d’extraire un Hook personnalisé pour chaque petit bout de code dupliqué. Certaines duplications sont acceptables. Par exemple, extraire un Hook `useFormInput` pour enrober un seul appel de `useState` comme précédemment est probablement inutile. -Cependant, à chaque fois que vous écrivez un effet, demandez-vous s’il ne serait pas plus clair de l’enrober également dans un Hook personnalisé. [Vous ne devriez pas avoir besoin d’effets si souvent,](/learn/you-might-not-need-an-effect) alors si vous en écrivez un, cela signifie que vous devez ???step outside??? « sortir » de React pour vous synchroniser avec un système externe ou pour faire quelque chose pour lequel React n’a pas une API intégrée. L’enrober dans un Hook personnalisé vous permet de communiquer précisément votre intention et la manière dont les flux de données circulent à travers lui. +Cependant, à chaque fois que vous écrivez un effet, demandez-vous s’il ne serait pas plus clair de l’enrober également dans un Hook personnalisé. [Vous ne devriez pas avoir besoin d’effets si souvent,](/learn/you-might-not-need-an-effect) alors si vous en écrivez un, ça signifie que vous devez « sortir » de React pour vous synchroniser avec un système externe ou pour faire quelque chose pour lequel React n’a pas une API intégrée. L’enrober dans un Hook personnalisé permet de communiquer précisément votre intention et la manière dont les flux de données circulent à travers lui. Prenons l’exemple d’un composant `ShippingForm` qui affiche deux listes déroulantes : l’une présente la liste des villes, l’autre affiche la liste des quartiers de la ville choisie. Vous pourriez démarrer avec un code ressemblant à ceci : @@ -1176,13 +1176,13 @@ function ShippingForm({ country }) { // ... ``` -Extraire un Hook personnalisé rend le flux des données explicite. Vous renseignez l’`url`, et vous obtenez le `data` en retour. En « cachant » votre effet dans `useData`, vous empêchez également que toute personne travaillant sur le composant `ShippingForm` d’y ajouter [des dépendances inutiles](/learn/removing-effect-dependencies). Avec le temps, la plupart des effets de votre app se trouveront dans des Hooks personnalisés. +Extraire un Hook personnalisé rend le flux des données explicite. Vous renseignez l’`url`, et vous obtenez le `data` en retour. En « cachant » votre effet dans `useData`, vous empêchez également qu’une autre personne travaillant sur le composant `ShippingForm` y ajoute [des dépendances inutiles](/learn/removing-effect-dependencies). Avec le temps, la plupart des effets de votre app se trouveront dans des Hooks personnalisés. -#### Gardez vos Hooks personnalisés centrés sur des cas d’utilisation de haut niveau {/*keep-your-custom-hooks-focused-on-concrete-high-level-use-cases*/} +#### Gardez vos Hooks personnalisés centrés sur des cas d’usage de haut niveau {/*keep-your-custom-hooks-focused-on-concrete-high-level-use-cases*/} -Commencez par choisir le nom de votre Hook personnalisé. Si vous avez du mal à choisir un nom clair, cela peut signifier que votre effet est trop lié au reste de la logique de votre composant, et qu’il n’est pas encore prêt à être extrait. +Commencez par choisir le nom de votre Hook personnalisé. Si vous avez du mal à choisir un nom clair, ça peut signifier que votre effet est trop lié au reste de la logique de votre composant, et qu’il n’est pas encore prêt à être extrait. Dans l’idéal, le nom de votre Hook personnalisé doit être suffisament clair pour qu’une personne qui n’écrit pas souvent du code puisse deviner ce que fait votre Hook personnalisé, ce qu’il prend et ce qu’il renvoie : @@ -1190,25 +1190,25 @@ Dans l’idéal, le nom de votre Hook personnalisé doit être suffisament clair * ✅ `useImpressionLog(eventName, extraData)` * ✅ `useChatRoom(options)` -Lorsque vous vous synchronisez avec un système externe, le nom de votre Hook personnalisé peut être plus technique et utiliser un jargon spécifique à ce système. C’est une bonne chose tant que cela reste clair pour une personne familière avec ce système : +Lorsque vous vous synchronisez avec un système externe, le nom de votre Hook personnalisé peut être plus technique et utiliser un jargon spécifique à ce système. C’est une bonne chose tant que ça reste clair pour une personne familière avec ce système : * ✅ `useMediaQuery(query)` * ✅ `useSocket(url)` * ✅ `useIntersectionObserver(ref, options)` -**Les Hooks personnalisés doivent restés focalisés sur des cas d’utilisation concrets de haut niveau.** Évitez de créer et d’utiliser de Hooks personnalisé de « cycle de vie » qui agissent comme des alternatives et des enrobage de commodité pour l’API `useEffect` elle-même : +**Les Hooks personnalisés doivent restés focalisés sur des cas d’usage concrets de haut niveau.** Évitez de créer et d’utiliser de Hooks personnalisé de « cycle de vie » qui agissent comme des alternatives et des enrobages de commodité pour l’API `useEffect` elle-même : * 🔴 `useMount(fn)` * 🔴 `useEffectOnce(fn)` * 🔴 `useUpdateEffect(fn)` -Par exemple, ce Hook `useMount` essaie de s’assurer que du code ne s’exécute qu’au « montage » : +Par exemple, ce Hook `useMount` essaie de s’assurer que du code ne s’exécute qu’au « montage » : ```js {4-5,14-15} function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); - // 🔴 À éviter : utiliser des Hooks personnalisés de « cycle de vie ». + // 🔴 À éviter : utiliser des Hooks personnalisés de « cycle de vie ». useMount(() => { const connection = createConnection({ roomId, serverUrl }); connection.connect(); @@ -1218,7 +1218,7 @@ function ChatRoom({ roomId }) { // ... } -// 🔴 À éviter : créer des Hooks personnalisés de « cycle de vie ». +// 🔴 À éviter : créer des Hooks personnalisés de « cycle de vie ». function useMount(fn) { useEffect(() => { fn(); @@ -1226,7 +1226,7 @@ function useMount(fn) { } ``` -**Les Hooks personnalisés de « cycle de vie » comme `useMount` ne s’intègrent pas bien dans le paradigme de React.** Par exemple, ce code contient une erreur (il ne « réagit » pas aux changements de `roomId` ou `serverUrl` changes), mais le linter ne vous avertira pas à ce sujet car le linter ne vérifie que les appels directs à `useEffect`. Il ne connaîtra rien de votre Hook. +**Les Hooks personnalisés de « cycle de vie » comme `useMount` ne s’intègrent pas bien dans le paradigme de React.** Par exemple, ce code contient une erreur (il ne « réagit » pas aux changements de `roomId` ou `serverUrl`), mais le linter ne vous avertira pas à ce sujet car il ne vérifie que les appels directs à `useEffect`. Il ne connaît rien de votre Hook. Si vous écrivez un effet, commencez par utiliser directement une API de React : @@ -1250,7 +1250,7 @@ function ChatRoom({ roomId }) { } ``` -Ensuite, vous pouvez (mais ce n’est pas obligatoire) extraire des Hooks personnalisés pour différents cas d’utilisation de haut niveau : +Ensuite, vous pouvez (mais ce n’est pas obligatoire) extraire des Hooks personnalisés pour différents cas d’usage de haut niveau : ```js function ChatRoom({ roomId }) { @@ -1263,13 +1263,13 @@ function ChatRoom({ roomId }) { } ``` -**Un bon Hook personnalisé rend le code appelé plus déclaratif en limitant ce qu’il fait.** Par exemple, `useChatRoom(options)` ne peut que se connecter à un salon de discussion, tandis que `useImpressionLog(eventName, extraData)` ne peut qu’envoyer les journaux à un système d’analytique. Si l’API de votre Hook personnalisé ne limite pas les cas d’utilisation et est très abstraite, elle risque d'introduire à long terme plus de problème qu’elle n’en résoudra. +**Un bon Hook personnalisé rend le code appelé plus déclaratif en limitant ce qu’il fait.** Par exemple, `useChatRoom(options)` ne peut que se connecter à un salon de discussion, tandis que `useImpressionLog(eventName, extraData)` ne peut qu’envoyer les journaux à un système d’analytique. Si l’API de votre Hook personnalisé ne limite pas les cas d’usage et est très abstraite, elle risque d’introduire à long terme plus de problèmes qu’elle n’en résoudra. ### Les Hooks personnalisés vous aident à migrer vers de meilleurs modèles {/*custom-hooks-help-you-migrate-to-better-patterns*/} -Les effets sont un [« échappatoire »](/learn/escape-hatches) : vous les utiliser quand vous avez besoin de « sortir » de React et quand il n’y a pas de meilleure solution intégrée pour votre cas d’utilisation. Avec le temps, le but de l’équipe de React est de limiter au minimum le nombre d’effets dans votre app en fournissant des solutions plus spécifiques à des problèmes plus spécifiques. Enrober vos effets dans des Hooks personnalisés facilite la mise à jour de votre code lorsque ces solutions deviennent disponibles. +Les effets sont un [« échappatoire »](/learn/escape-hatches) : vous les utilisez quand vous avez besoin de « sortir » de React et quand il n’y a pas de meilleure solution intégrée pour votre cas d’usage. Avec le temps, le but de l’équipe de React est de réduire au maximum le nombre d’effets dans votre app en fournissant des solutions plus spécifiques à des problèmes plus spécifiques. Enrober vos effets dans des Hooks personnalisés facilite la mise à jour de votre code lorsque ces solutions deviennent disponibles. Revenons à cet exemple : @@ -1332,7 +1332,7 @@ export function useOnlineStatus() { -Dans l’exemple ci-dessus, `useOnlineStatus` est implémentée avec une paire de [`useState`](/reference/react/useState) et [`useEffect`.](/reference/react/useEffect) Cependant, ce n’est pas la meilleure solution possible. Elle ne tient pas compte d’un certain nombre de cas limites. Par exemple, elle suppose que lorsque le composant est monté, `isOnline` est déjà à `true`, mais cela peut être faux si le réseau a déjà été mis hors ligne. Vous pouvez utiliser l’API du navigateur [`navigator.onLine`](https://developer.mozilla.org/fr/docs/Web/API/Navigator/onLine) pour vérifier cela, mais l’utiliser directement ne marchera pas sur le serveur pour générer le HTML initial. En bref, ce code peut être amélioré. +Dans l’exemple ci-dessus, `useOnlineStatus` est implémentée avec une paire de [`useState`](/reference/react/useState) et [`useEffect`.](/reference/react/useEffect) Cependant, ce n’est pas la meilleure solution possible. Elle ne tient pas compte d’un certain nombre de cas limites. Par exemple, elle suppose que lorsque le composant est monté, `isOnline` est déjà à `true`, mais ça peut être faux si le réseau a déjà été mis hors ligne. Vous pouvez utiliser l’API du navigateur [`navigator.onLine`](https://developer.mozilla.org/fr/docs/Web/API/Navigator/onLine) pour vérifier ça, mais l’utiliser directement ne marchera pas sur le serveur pour générer le HTML initial. En bref, ce code peut être amélioré. Heureusement, React 18 inclut une API dédiée appelée [`useSyncExternalStore`](/reference/react/useSyncExternalStore) qui se charge de tous ces problèmes pour vous. Voici comment votre Hook personnalisé `useOnlineStatus` est réécrit pour tirer avantage de cette nouvelle API : @@ -1414,7 +1414,7 @@ C’est une raison pour laquelle il est souvent utile d’enrober des effets dan 2. Vous permettez à vos composants de se concentrer sur l’intention plutôt que sur l’implémentation exacte de vos effets. 3. Lorsque React ajoute de nouvelles fonctionnalités, vous pouvez retirer ces effets sans changer aucun de vos composants. -À la manière d’un [système de design,](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969) vous pourriez trouver utile de commencer à extraire les idiomes communs des composants de votre app dans des Hooks personnalisés. Cela le code de vos composants restera centré sur l’intention et vous éviterez la plupart du temps d’utiliser des effets bruts. De nombreux Hooks personnalisés de qualité sont maintenus par la communauté de React. +À la manière d’un [système de design,](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969) vous pourriez trouver utile de commencer à extraire les idiomes communs des composants de votre app dans des Hooks personnalisés. Ainsi, le code de vos composants restera centré sur l’intention et vous éviterez la plupart du temps d’utiliser des effets bruts. De nombreux Hooks personnalisés de qualité sont maintenus par la communauté de React. @@ -1432,13 +1432,13 @@ function ShippingForm({ country }) { // ... ``` -Si vous utilisez des Hooks personnalisés comme le `useData` plus haut dans votre app, la migration vers l’approche éventuellement recommandée nécessitera moins de changements que si vous écrivez manuellement des effets bruts dans chaque composant. Cependant, l’ancienne approche continuera de bien fonctionner, donc si vous vous sentez à l’aise en écrivant des effets bruts, vous pouvez continuer ainsi. +Si vous utilisez des Hooks personnalisés comme le `useData` plus haut dans votre app, la migration vers l’approche finalement recommandée nécessitera moins de changements que si vous écrivez manuellement des effets bruts dans chaque composant. Cependant, l’ancienne approche continuera de bien fonctionner, donc si vous vous sentez à l’aise en écrivant des effets bruts, vous pouvez continuer ainsi. ### Il y a plus d’une façon de faire {/*there-is-more-than-one-way-to-do-it*/} -Supposons que vous voulez implémenter une animation de fondu-enchaîné *en partant de zéro* en utilisant l’API du navigateur [`requestAnimationFrame`](https://developer.mozilla.org/fr/docs/Web/API/window/requestAnimationFrame). Vous pouvez commencer par un effet qui initialise une boucle d’animation. Pendant chaque image de l’animation, vous pourriez changer l’opacité du nœud du DOM si vous le [conservez dans une ref](/learn/manipulating-the-dom-with-refs) jusqu’à ce qu’il atteigne `1`. Votre code pourrait commencer ainsi : +Supposons que vous voulez implémenter une animation de fondu-enchaîné *en partant de zéro* en utilisant l’API du navigateur [`requestAnimationFrame`](https://developer.mozilla.org/fr/docs/Web/API/window/requestAnimationFrame). Vous pouvez commencer par un effet qui initialise une boucle d’animation. Pendant chaque image de l’animation, vous pourriez changer l’opacité du nœud du DOM si vous le [conservez dans une ref](/learn/manipulating-the-dom-with-refs) jusqu’à ce qu’elle atteigne `1`. Votre code pourrait commencer ainsi : @@ -1716,7 +1716,7 @@ html, body { min-height: 300px; } -Cependant, vous n’avez pas *besoin* de faire ça. Comme pour les fonctions ordinaires, c’est finalement à vous de définir les limites entre les différentes parties de votre code. Vous pouvez également adopter une approche tout à fait différente. Au lieu de conserver votre logique dans un effet, vous pouvez déplacer la plupart de la logique impérative dans une [classe](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Classes) JavaScript : +Cependant, vous n’avez pas *besoin* de faire ça. Comme pour les fonctions ordinaires, c’est finalement à vous de définir les frontières entre les différentes parties de votre code. Vous pouvez également adopter une approche tout à fait différente. Au lieu de conserver votre logique dans un effet, vous pouvez déplacer la plupart de la logique impérative dans une [classe](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Classes) JavaScript : @@ -1814,7 +1814,7 @@ html, body { min-height: 300px; } -Les effets permettent à React de se connecter à des systèmes externes. Plus la coordination entre les effets est nécessaire (par exemple pour enchaîner des animations multiples), plus il est sensé d’extraire *complètement* cette logique des effets et des Hooks, comme dans le bac à sable ci-dessus. Le code extrait *devient* ainsi le « système externe ». Cela permet à vos effets de rester simple car ils n’auront qu’à envoyer des messages au système que vous avez sorti de React. +Les effets permettent à React de se connecter à des systèmes externes. Plus la coordination entre les effets est nécessaire (par exemple pour enchaîner des animations multiples), plus il est sensé d’extraire *complètement* cette logique des effets et des Hooks, comme dans le bac à sable ci-dessus. Le code extrait *devient* ainsi le « système externe ». Ça permet à vos effets de rester simples car ils n’auront qu’à envoyer des messages au système que vous avez sorti de React. Les exemples ci-dessus supposent que la logique de fondu-enchaîné soit écrite en JavaScript. Cependant, cette animation particulière de fondu-enchaîné est à la fois plus simple et beaucoup plus efficace lorsqu’elle est écrite par une simple [animation CSS :](https://developer.mozilla.org/fr/docs/Web/CSS/CSS_Animations/Using_CSS_animations) @@ -1883,24 +1883,24 @@ Parfois, vous n’avez même pas besoin d’un Hook ! - Le code de vos Hooks personnalisés doit être pur, comme le code de votre composant. - Enrobez les gestionnaires d’événements reçus par les Hooks personnalisés dans des événéments d’effet. - Ne créez pas des Hooks personnalisés comme `useMount`. Veillez à ce que leur objectif soit spécifique. -- C’est à vous de décider comment et où choisir les limites de votre code. +- C’est à vous de décider comment et où définir les frontières de votre code. -#### Extract a `useCounter` Hook {/*extract-a-usecounter-hook*/} +#### Extraire un Hook `useCounter` {/*extract-a-usecounter-hook*/} -This component uses a state variable and an Effect to display a number that increments every second. Extract this logic into a custom Hook called `useCounter`. Your goal is to make the `Counter` component implementation look exactly like this: +Ce composant utilise une variable d’état et un effet pour afficher un nombre qui s’incrémente à chaque seconde. Extrayez cette logique dans un Hook personnalisé appelé `useCounter`. Votre but est de faire que l’implémentation du composant `Counter` ressemble exactement à ça : ```js export default function Counter() { const count = useCounter(); - return

Seconds passed: {count}

; + return

Secondes écoulées : {count}

; } ``` -You'll need to write your custom Hook in `useCounter.js` and import it into the `Counter.js` file. +Vous devrez écrire votre Hook personnalisé dans `useCounter.js` et l’importer dans le fichier `Counter.js`. @@ -1915,19 +1915,19 @@ export default function Counter() { }, 1000); return () => clearInterval(id); }, []); - return

Seconds passed: {count}

; + return

Secondes écoulées : {count}

; } ``` ```js useCounter.js -// Write your custom Hook in this file! +// Écrivez votre Hook personnalisé dans ce fichier ! ```
-Your code should look like this: +Votre code doit ressembler à ceci : @@ -1936,7 +1936,7 @@ import { useCounter } from './useCounter.js'; export default function Counter() { const count = useCounter(); - return

Seconds passed: {count}

; + return

Secondes écoulées : {count}

; } ``` @@ -1957,13 +1957,13 @@ export function useCounter() {
-Notice that `App.js` doesn't need to import `useState` or `useEffect` anymore. +Remarquez que `App.js` n’a plus besoin d’importer `useState` ni `useEffect` désormais.
-#### Make the counter delay configurable {/*make-the-counter-delay-configurable*/} +#### Rendez le délai du compteur configurable {/*make-the-counter-delay-configurable*/} -In this example, there is a `delay` state variable controlled by a slider, but its value is not used. Pass the `delay` value to your custom `useCounter` Hook, and change the `useCounter` Hook to use the passed `delay` instead of hardcoding `1000` ms. +Dans cet exemple, il y a une variable d’état `delay` qui est contrôlée par un curseur, mais sa valeur n’est pas utilisée. Passez la valeur de `delay` à votre Hook personnalisé, et changez le Hook `useCounter` pour utiliser le `delay` passé plutôt que les `1000` ms codés en dur. @@ -1977,7 +1977,7 @@ export default function Counter() { return ( <>
-

Ticks: {count}

+

Ticks : {count}

); } @@ -2013,7 +2013,7 @@ export function useCounter() { -Pass the `delay` to your Hook with `useCounter(delay)`. Then, inside the Hook, use `delay` instead of the hardcoded `1000` value. You'll need to add `delay` to your Effect's dependencies. This ensures that a change in `delay` will reset the interval. +Passez le `delay` à votre Hook avec `useCounter(delay)`. À l’intérieur de votre Hook, utilisez ensuite `delay` à la place de la valeur `1000` codée en dur. Vous devrez ajouter `delay` aux dépendances de votre effet. Ça garantira qu’un changement de `delay` réinitialisera l’intervalle. @@ -2027,7 +2027,7 @@ export default function Counter() { return ( <> -Essayez d’activer et de désactiver votre réseau et remarquez comment cette `StatusBar` se met à jour en fonction de vos actions. +Essayez d’activer et de désactiver votre réseau et voyez comme cette `StatusBar` se met à jour en fonction de vos actions. Imaginez maintenant que vous souhaitiez utiliser la *même* logique dans un composant différent. Vous voulez créer un bouton Enregistrer qui sera désactivé et affichera « Reconnexion… » au lieu de « Enregistrer » lorsque le réseau est désactivé. -Pour commencer, vous pouvez copier et coller l’état `isOnline` et l’effet dans le `SaveButton` : +Pour commencer, vous pouvez copier-coller l’état `isOnline` et l’effet dans le `SaveButton` : @@ -96,9 +96,9 @@ export default function SaveButton() { -Vérifiez que le bouton changera d’apparence si vous éteignez le réseau. +Vérifiez que le bouton changera d’apparence si vous débranchez le réseau. -Ces deux composants fonctionnent bien, mais la duplication de la logique entre eux est regrettable. Il semble que s’ils ont un *aspect visuel* différent, ils réutilisent la même logique. +Ces deux composants fonctionnent bien, mais la duplication de la logique entre eux est regrettable. Il semble que même s’ils ont un *aspect visuel* différent, ils réutilisent la même logique. ### Extraire votre Hook personnalisé d’un composant {/*extracting-your-own-custom-hook-from-a-component*/} @@ -125,7 +125,7 @@ function SaveButton() { } ``` -Même s’il n’existe pas un tel Hook intégré, vous pouvez l’écrire vous-même. Déclarez une fonction appelée `useOnlineStatus` et déplacez-y tout le code dupliqué des composants que vous avez écrits plus tôt : +Même si un tel Hook intégré n’existe pas, vous pouvez l’écrire vous-même. Déclarez une fonction appelée `useOnlineStatus` et déplacez-y tout le code dupliqué des composants que vous avez écrits plus tôt : ```js {2-16} function useOnlineStatus() { @@ -209,22 +209,22 @@ export function useOnlineStatus() {
-Vérifiez que l’activation et la désactivation du réseau met à jour les deux composants. +Vérifiez que l’activation et la désactivation du réseau mettent à jour les deux composants. -Désormais, Vos composants n’ont plus de logique répétitive. **Plus important encore, le code qu’ils contiennent décrivent *ce qu’ils veulent faire* (utiliser le statut de connexion) plutôt que *la manière de le faire* (en s’abonnant aux événements du navigateur).** +Désormais, vos composants n’ont plus de logique dupliquée. **Plus important encore, le code qu’ils contiennent décrit *ce qu’ils veulent faire* (utiliser le statut de connexion) plutôt que *la manière de le faire* (en s’abonnant aux événements du navigateur).** -Quand vous extrayez la logique dans des Hooks personnalisés, vous pouvez cacher les détails de la façon dont vous traitez avec des systèmes externes ou avec une API du navigateur. Le code de vos composants expriment votre intention, pas l’implémentation. +Quand vous extrayez la logique dans des Hooks personnalisés, vous pouvez masquer les détails de la façon dont vous traitez avec des systèmes extérieurs ou avec une API du navigateur. Le code de vos composants exprime votre intention, pas l’implémentation. ### Les noms des Hooks commencent toujours par `use` {/*hook-names-always-start-with-use*/} -Les applications React sont constuites à partir de composants. Les composants sont construits à partir des Hooks, qu’ils soient intégrés ou personnalisés. Vous utiliserez probablement souvent des Hooks personnalisés créés par d’autres, mais vous pourrez occasionnellement en écrire un vous-même ! +Les applications React sont construites à partir de composants. Les composants sont construits à partir des Hooks, qu’ils soient pré-fournis ou personnalisés. Vous utiliserez probablement souvent des Hooks personnalisés créés par d’autres, mais vous pourrez occasionnellement en écrire un vous-même ! Vous devez respecter les conventions de nommage suivantes : -1. **Le nom des composants React doit commencer par une lettre en majuscule,** comme `StatusBar` et `SaveButton`. Les composants React doivent également renvoyer quelque chose que React sait afficher, comme un morceau de JSX. -2. **Le nom des Hook doit commencer par `use` suivi d’une majuscule,** comme [`useState`](/reference/react/useState) (intégré) ou `useOnlineStatus` (personnalisé, comme plus haut dans la page). Les Hooks peuvent renvoyer des valeurs arbitraires. +1. **Les noms des composants React doivent commencer par une majuscule,** comme `StatusBar` et `SaveButton`. Les composants React doivent également renvoyer quelque chose que React sait afficher, comme un bout de JSX. +2. **Les noms des Hooks doivent commencer par `use` suivi d’une majuscule,** comme [`useState`](/reference/react/useState) (fourni) ou `useOnlineStatus` (personnalisé, comme plus haut dans cette page). Les Hooks peuvent renvoyer des valeurs quelconques. -Cette convention garantit que vous pouvez toujours regarder un composant et savoir où son état, ses effets et d’autres fonctionnalités de React peuvent se « cacher ». Par exemple, si vous voyez un appel à la fonction `getColor()` dans votre composant, vous pouvez être sûr qu’il ne contient pas d’état React car son nom ne commence pas par `use`. Cependant, un appel de fonction comme `useOnlineStatus()` contiendra très probablement des appels à d’autres Hooks à l’intérieur. +Cette convention garantit que vous pouvez toujours examiner le code d’un composant et repérer où son état, ses effets et d’autres fonctionnalités de React peuvent « se cacher ». Par exemple, si vous voyez un appel à la fonction `getColor()` dans votre composant, vous pouvez être sûr·e qu’il ne contient pas d’état React car son nom ne commence pas par `use`. En revanche, un appel de fonction comme `useOnlineStatus()` contiendra très probablement des appels à d’autres Hooks. @@ -234,38 +234,38 @@ Si votre linter est [configuré pour React,](/learn/editor-setup#linting) il app -#### Toutes les fonctions appelées pendant le rendu doivent-elles commencer par le préfixe use ? {/*should-all-functions-called-during-rendering-start-with-the-use-prefix*/} +#### Toutes les fonctions appelées pendant le rendu doivent-elles commencer par le préfixe `use` ? {/*should-all-functions-called-during-rendering-start-with-the-use-prefix*/} Non. Les fonctions qui n’*appellent* pas des Hooks n’ont pas besoin d’*être* des Hooks. -Si votre fonction n’appelle aucun Hook, évitez d’utiliser le préfixe `use`. À la place, écrivez une fonction normale *sans* le préfixe `use`. Par exemple, `useSorted` ci-dessous n’appelle pas de Hook, appelez-la `getSorted` à la place : +Si votre fonction n’appelle aucun Hook, évitez d’utiliser le préfixe `use`. À la place, écrivez une fonction normale *sans* le préfixe `use`. Par exemple, `useSorted` ci-dessous n’appelle pas de Hook, appelez-la plutôt `getSorted` : ```js -// 🔴 À éviter : un Hook qui n’utilise pas d’autre Hooks. +// 🔴 À éviter : un Hook qui n’utilise pas d’autres Hooks function useSorted(items) { return items.slice().sort(); } -// ✅ Correct : une fonction normale qui n’utilise pas de Hook. +// ✅ Correct : une fonction normale qui n’utilise pas de Hook function getSorted(items) { return items.slice().sort(); } ``` -Ça garantit que votre code peut appeler cette fonction normale n’importe où, y compris dans ces conditions : +Ça garantit que votre code peut appeler cette fonction normale n’importe où, y compris dans des conditions : ```js function List({ items, shouldSort }) { let displayedItems = items; if (shouldSort) { - // ✅ Il est possible d’appeler getSorted() conditionnellement parce qu’il ne s’agit pas d’un Hook. + // ✅ On peut appeler getSorted() conditionnellement parce qu’il ne s’agit pas d’un Hook displayedItems = getSorted(items); } // ... } ``` -Vous devez utiliser le préfixe `use` pour une fonction (en ainsi en faire un Hook) si elle utilise elle-même un Hook : +Vous devez utiliser le préfixe `use` pour une fonction (et ainsi, en faire un Hook) si elle utilise elle-même un Hook : ```js // ✅ Correct : un Hook qui utilise un autre Hook @@ -274,7 +274,7 @@ function useAuth() { } ``` -Techniquement, cette règle n’est pas dictée par React. En principe, vous pouvez créer un Hook qui n’appelle pas d’autres Hooks. C’est souvent déroutant et limitant, aussi est-il préférable d’éviter ce modèle. Cependant, il peut y avoir de rares cas où c’est utile. Par exemple, votre fonction n’appelle pas encore de Hook pour l’instant, mais vous prévoyez d’y ajouter des appels à des Hooks dans le futur. Il est alors logique d’utiliser le préfixe `use` : +Techniquement, cette règle n’est pas vérifiée par React. En principe, vous pouvez créer un Hook qui n’appelle pas d’autres Hooks. C’est souvent déroutant et limitant, aussi est-il préférable d’éviter cette approche. Cependant, il peut y avoir de rares cas où c’est utile. Par exemple, votre fonction n’appelle pas encore de Hook, mais vous prévoyez d’y ajouter des appels à des Hooks à l'avenir. Il est alors logique d’utiliser le préfixe `use` : ```js {3-4} @@ -286,13 +286,13 @@ function useAuth() { } ``` -Les composants ne pourront pas l’appeler de manière conditionnelle. Ça deviendra important quand vous ajouterez des appels à des Hooks à l’intérieur. Si vous ne prévoyez pas d’appeler des Hooks à l’intérieur (ni maintenant ni plus tard), alors n’en faites pas un Hook. +Les composants ne pourront pas l’appeler de manière conditionnelle. Ça deviendra important quand vous ajouterez des appels à des Hooks à l’intérieur. Si vous ne prévoyez pas d’appeler des Hooks dans votre fonction (ni maintenant ni plus tard), alors n’en faites pas un Hook. ### Les Hooks personnalisés vous permettent de partager la logique d’état, mais pas l’état lui-même {/*custom-hooks-let-you-share-stateful-logic-not-state-itself*/} -Dans l’exemple précédent, lorsque vous avez activé et désactivé le réseau, les deux composants ont été mis à jour ensemble. Cependant, il est faux de penser qu’une seule variable d’état `isOnline` est partagée entre eux. Regardez ce code : +Dans l’exemple précédent, lorsque vous avez activé et désactivé le réseau, les deux composants se sont mis à jour ensemble. Cependant, ne croyez pas qu’une seule variable d’état `isOnline` est partagée entre eux. Regardez ce code : ```js {2,7} function StatusBar() { @@ -326,9 +326,9 @@ function SaveButton() { } ``` -Il s’agit de deux variables d’état et effets totalement indépendants ! Il se trouve qu’ils ont la même valeur au même moment parce que vous les avez synchronisés entre eux par la même valeur externe (si le réseau est activé). +Il s’agit de deux variables d’état et effets totalement indépendants ! Il se trouve qu’ils ont la même valeur au même moment parce que vous les avez synchronisés avec la même donnée extérieure (le fait que le réseau est actif ou non). -Pour mieux illustrer ceci, nous allons avoir besoin d’un exemple différent. Considérez ce composant `Form` : +Pour mieux illustrer ça, nous allons avoir besoin d’un exemple différent. Examinez ce composant `Form` : @@ -370,13 +370,13 @@ input { margin-left: 10px; } -Il y a une logique répétitive pour chaque champ du formulaire : +Il y a de la logique répétée pour chaque champ du formulaire : 1. Il y a un élément d’état (`firstName` et `lastName`). 1. Il y a un gestionnaire de changement (`handleFirstNameChange` et `handleLastNameChange`). -1. Il y a un morceau de JSX qui spécifie les attributs `value` et `onChange` pour ce champ. +1. Il y a un bout de JSX qui spécifie les attributs `value` et `onChange` pour ce champ. -Vous pouvez extraire la logique répétitive dans ce Hook personnalisé `useFormInput` : +Vous pouvez extraire la logique dupliquée dans ce Hook personnalisé `useFormInput` : @@ -431,7 +431,7 @@ input { margin-left: 10px; } Notez qu’il ne déclare qu’*une* seule variable d’état appelée `value`. -Cependant, le composant `Form` appelle `useFormInput` *deux fois :* +Pourtant, le composant `Form` appelle `useFormInput` *deux fois :* ```js function Form() { @@ -444,13 +444,13 @@ C’est pourquoi ça revient à déclarer deux variables d’état distinctes ! **Les Hooks personnalisés vous permettent de partager *la logique d’état* et non *l’état lui-même.* Chaque appel à un Hook est complètement indépendant de tous les autres appels au même Hook.** C’est pourquoi les deux bacs à sable ci-dessus sont totalement équivalents. Si vous le souhaitez, revenez en arrière et comparez-les. Le comportement avant et après l’extraction d’un Hook personnalisé est identique. -Lorsque vous avez besoin de partager l’état lui-même entre plusieurs composants, [faites-le remonter puis transmettez-le](/learn/sharing-state-between-components) à la place. +Lorsque vous avez besoin de partager l’état lui-même entre plusieurs composants, [faites-le plutôt remonter puis transmettez-le](/learn/sharing-state-between-components). ## Transmettre des valeurs réactives entre les Hooks {/*passing-reactive-values-between-hooks*/} -Le code contenu dans vos Hooks personnalisés sera réexécuté à chaque nouvel affichage de votre composant. C’est pourquoi, comme les composants, les Hooks personnalisés [doivent être purs.](/learn/keeping-components-pure) Considérez le code des Hooks personnalisés comme une partie du corps de votre composant ! +Le code contenu dans vos Hooks personnalisés sera réexécuté à chaque nouveau rendu de votre composant. C’est pourquoi, comme les composants, les Hooks personnalisés [doivent être purs](/learn/keeping-components-pure). Considérez le code des Hooks personnalisés comme une partie du corps de votre composant ! -Comme les Hooks personnsalisés sont réaffichés en même temps que votre composant, ils reçoivent toujours les props et l’état les plus récents. Pour comprendre ce que ça signifie, prenez cet exemple de salon de discussion. Changez l’URL du serveur ou le salon de discussion : +Comme les Hooks personnalisés sont mis à jour au sein du rendu de votre composant, ils reçoivent toujours les props et l’état les plus récents. Pour comprendre ce que ça signifie, prenez cet exemple de salon de discussion. Changez l’URL du serveur ou le salon de discussion : @@ -528,7 +528,7 @@ export function createConnection({ serverUrl, roomId }) { let messageCallback; return { connect() { - console.log('✅ Connexion au salon "' + roomId + '" depuis ' + serverUrl + '...'); + console.log('✅ Connexion au salon « ' + roomId + ' » depuis ' + serverUrl + '...'); clearInterval(intervalId); intervalId = setInterval(() => { if (messageCallback) { @@ -543,7 +543,7 @@ export function createConnection({ serverUrl, roomId }) { disconnect() { clearInterval(intervalId); messageCallback = null; - console.log('❌ Déconnexion du salon "' + roomId + '" depuis ' + serverUrl + ''); + console.log('❌ Déconnexion du salon « ' + roomId + ' » depuis ' + serverUrl + ''); }, on(event, callback) { if (messageCallback) { @@ -621,7 +621,7 @@ export function useChatRoom({ serverUrl, roomId }) { } ``` -Ça permet à votre composant `ChatRoom` d’appeler le Hook personnalisé sans se préoccuper de la façon dont il fonctionne à l’intérieur. +Ça permet à votre composant `ChatRoom` d’appeler le Hook personnalisé sans se préoccuper de la façon dont il fonctionne en interne. ```js {4-7} export default function ChatRoom({ roomId }) { @@ -646,7 +646,7 @@ export default function ChatRoom({ roomId }) { C’est plus simple ainsi ! (Mais ça fait toujours la même chose.) -Remarquez que la logique *répond toujours* aux changement des props et de l’état. Essayez de modifier l’URL du serveur ou le salon choisi : +Remarquez que la logique *réagit toujours* aux changement des props et de l’état. Essayez de modifier l’URL du serveur ou le salon choisi : @@ -808,7 +808,7 @@ button { margin-left: 10px; } -Remarquez comment vous récupérez la valeur retournée par un Hook : +Voyez comme vous récupérez la valeur retournée par un Hook : ```js {2} export default function ChatRoom({ roomId }) { @@ -834,7 +834,7 @@ export default function ChatRoom({ roomId }) { // ... ``` -Chaque vois que votre composant `ChatRoom` est réaffiché, il passe les dernières valeurs de `roomId` et `serverUrl` à votre Hook. Ceci explique pourquoi votre effet se reconnecte au salon à chaque fois que leurs valeurs sont différentes après un nouveal affichage. (Si vous avez déjà travaillé avec des logiciels de traitement d’audio ou de vidéo, ce type d’enchaînement de Hooks peut vous rappeler l’enchaînement d’effets visuels ou sonores. C’est comme si le retour de `useState` « alimentait » l’entrée de `useChatRoom`.) +Chaque fois que votre composant `ChatRoom` refait un rendu, il passe les dernières valeurs de `roomId` et `serverUrl` à votre Hook. Ça explique pourquoi votre effet se reconnecte au salon à chaque fois que leurs valeurs sont différentes après un nouveau rendu. (Si vous avez déjà travaillé avec des logiciels de traitement d’audio ou de vidéo, ce type d’enchaînement de Hooks peut vous rappeler l’enchaînement d’effets visuels ou sonores. C’est comme si le résultat en sortie de `useState` était « branché » en entrée de `useChatRoom`.) ### Transmettre des gestionnaires d’événements à des Hooks personnalisés {/*passing-event-handlers-to-custom-hooks*/} @@ -844,7 +844,7 @@ Cette section décrit une **API expérimentale qui n’a pas encore été livré -Lorsque vous commencez à utiliser `useChatRoom` dans un nombre plus important de composants, vous souhaitez peut-être que ces derniers puissent personnaliser son comportement. Par exemple, pour l’instant la logique de ce qu’il faut faire quand un message arrive est codée en dur à l’intérieur du Hook : +Lorsque vous commencerez à utiliser `useChatRoom` dans davantage de composants, vous souhaiterez peut-être que ces derniers puissent personnaliser son comportement. Par exemple, pour l’instant la logique de traitement quand un message arrive est codée en dur à l’intérieur du Hook : ```js {9-11} export function useChatRoom({ serverUrl, roomId }) { @@ -863,7 +863,7 @@ export function useChatRoom({ serverUrl, roomId }) { } ``` -Disons que vous voulez déplacer cette logique à nouveau dans votre composant : +Disons que vous voulez ramener cette logique dans votre composant : ```js {7-9} export default function ChatRoom({ roomId }) { @@ -879,7 +879,7 @@ export default function ChatRoom({ roomId }) { // ... ``` -Pour que ça fonctionne, modifiez votre Hook personnalisé pour qu’il prenne `onReceiveMessage` comme l’une de ses options : +Pour que ça fonctionne, modifiez votre Hook personnalisé afin qu’il prenne `onReceiveMessage` comme l’une de ses options : ```js {1,10,13} export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { @@ -900,7 +900,7 @@ export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { Ça fonctionnera, mais il y a une autre amélioration que vous pouvez apporter quand votre Hook personnalisé accepte des gestionnaires d’événements. -Ajouter une dépendance à `onReceiveMessage` n’est pas idéal car il entraînera une reconnexion au salon à chaque réaffichage du composant. [Enrobez ce gestionnaire d’état dans un événement d’effet pour le supprimer des dépendances :](/learn/removing-effect-dependencies#wrapping-an-event-handler-from-the-props) +Ajouter une dépendance envers `onReceiveMessage` n’est pas idéal car elle entraînera une reconnexion au salon à chaque réaffichage du composant. [Enrobez ce gestionnaire d’état dans un Événement d’Effet pour le retirer des dépendances](/learn/removing-effect-dependencies#wrapping-an-event-handler-from-the-props) : ```js {1,4,5,15,18} import { useEffect, useEffectEvent } from 'react'; @@ -924,7 +924,7 @@ export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { } ``` -Maintenant, le salon ne se reconnectera plus à chaque réaffichage du composant `ChatRoom`. Voici une démonstration complète du passage d’un gestionnaire d’événement à un Hook personnalisé avec laquelle vous pouvez jouer : +À présent, le salon ne se reconnectera plus à chaque réaffichage du composant `ChatRoom`. Voici une démonstration complète du passage d’un gestionnaire d’événement à un Hook personnalisé avec laquelle vous pouvez jouer : @@ -1092,13 +1092,13 @@ button { margin-left: 10px; } -Remarquez que vous n’avez plus besoin de savoir *comment* `useChatRoom` fonctionne pour pouvoir l’utiliser. Vous pourriez l’ajouter à n’importe quel autre composant, lui passer n’importe quelles autres options, il fonctionnerait de la même manière. C’est la puissance des Hooks personnalisés. +Remarquez que vous n’avez plus besoin de savoir *comment* `useChatRoom` fonctionne pour pouvoir l’utiliser. Vous pourriez l’ajouter à n’importe quel autre composant, lui passer n’importe quelles autres options, il fonctionnerait de la même manière. C’est là toute la puissance des Hooks personnalisés. -## Quand utiliser des Hooks personnalisés {/*when-to-use-custom-hooks*/} +## Quand utiliser des Hooks personnalisés ? {/*when-to-use-custom-hooks*/} Il n’est pas nécessaire d’extraire un Hook personnalisé pour chaque petit bout de code dupliqué. Certaines duplications sont acceptables. Par exemple, extraire un Hook `useFormInput` pour enrober un seul appel de `useState` comme précédemment est probablement inutile. -Cependant, à chaque fois que vous écrivez un effet, demandez-vous s’il ne serait pas plus clair de l’enrober également dans un Hook personnalisé. [Vous ne devriez pas avoir besoin d’effets si souvent,](/learn/you-might-not-need-an-effect) alors si vous en écrivez un, ça signifie que vous devez « sortir » de React pour vous synchroniser avec un système externe ou pour faire quelque chose pour lequel React n’a pas une API intégrée. L’enrober dans un Hook personnalisé permet de communiquer précisément votre intention et la manière dont les flux de données circulent à travers lui. +Cependant, à chaque fois que vous écrivez un Effet, demandez-vous s’il ne serait pas plus clair de l’enrober également dans un Hook personnalisé. [Vous ne devriez pas avoir si souvent besoin d’Effets](/learn/you-might-not-need-an-effect), alors si vous en écrivez un, ça signifie que vous devez « sortir » de React pour vous synchroniser avec un système extérieur ou pour faire une chose pour laquelle React n’a pas d’API intégrée. L’enrober dans un Hook personnalisé permet de communiquer précisément votre intention et la manière dont les flux de données circulent à travers lui. Prenons l’exemple d’un composant `ShippingForm` qui affiche deux listes déroulantes : l’une présente la liste des villes, l’autre affiche la liste des quartiers de la ville choisie. Vous pourriez démarrer avec un code ressemblant à ceci : @@ -1142,7 +1142,7 @@ function ShippingForm({ country }) { // ... ``` -Bien que ce code soit assez répétitif, [il est acceptable de garder ces effets séparés les uns des autres.](/learn/removing-effect-dependencies#is-your-effect-doing-several-unrelated-things) Ils synchronisent deux choses différentes, vous ne devez donc pas les fusionner en un seul effet. À la place, vous pouvez simplifier le composant `ShippingForm` ci-dessus en sortant la logique commune entre eux dans votre propre Hook `useData` : +Bien que ce code soit assez répétitif, [il est légitime de garder ces effets séparés les uns des autres](/learn/removing-effect-dependencies#is-your-effect-doing-several-unrelated-things). Ils synchronisent deux choses différentes, vous ne devez donc pas les fusionner en un seul Effet. Vous pouvez plutôt simplifier le composant `ShippingForm` ci-dessus en sortant la logique commune entre eux dans votre propre Hook `useData` : ```js {2-18} function useData(url) { @@ -1166,7 +1166,7 @@ function useData(url) { } ``` -Vous pouvez maintenant remplacer les deux effets du composant `ShippingForm` par des appels à `useData` : +Vous pouvez maintenant remplacer les deux Effets du composant `ShippingForm` par des appels à `useData` : ```js {2,4} function ShippingForm({ country }) { @@ -1176,27 +1176,27 @@ function ShippingForm({ country }) { // ... ``` -Extraire un Hook personnalisé rend le flux des données explicite. Vous renseignez l’`url`, et vous obtenez le `data` en retour. En « cachant » votre effet dans `useData`, vous empêchez également qu’une autre personne travaillant sur le composant `ShippingForm` y ajoute [des dépendances inutiles](/learn/removing-effect-dependencies). Avec le temps, la plupart des effets de votre app se trouveront dans des Hooks personnalisés. +Extraire un Hook personnalisé rend le flux des données explicite. Vous renseignez l’`url`, et vous obtenez les `data` en retour. En « masquant » votre Effet dans `useData`, vous empêchez également qu’une autre personne travaillant sur le composant `ShippingForm` y ajoute [des dépendances inutiles](/learn/removing-effect-dependencies). Avec le temps, la plupart des Effets de votre appli se trouveront dans des Hooks personnalisés. -#### Gardez vos Hooks personnalisés centrés sur des cas d’usage de haut niveau {/*keep-your-custom-hooks-focused-on-concrete-high-level-use-cases*/} +#### Réservez vos Hooks personnalisés à des cas d’usage concrets de haut niveau {/*keep-your-custom-hooks-focused-on-concrete-high-level-use-cases*/} Commencez par choisir le nom de votre Hook personnalisé. Si vous avez du mal à choisir un nom clair, ça peut signifier que votre effet est trop lié au reste de la logique de votre composant, et qu’il n’est pas encore prêt à être extrait. -Dans l’idéal, le nom de votre Hook personnalisé doit être suffisament clair pour qu’une personne qui n’écrit pas souvent du code puisse deviner ce que fait votre Hook personnalisé, ce qu’il prend et ce qu’il renvoie : +Dans l’idéal, le nom de votre Hook personnalisé doit être suffisamment clair pour qu’une personne qui n’écrit pas souvent du code puisse deviner ce que fait votre Hook, ce qu’il prend en arguments et ce qu’il renvoie : * ✅ `useData(url)` * ✅ `useImpressionLog(eventName, extraData)` * ✅ `useChatRoom(options)` -Lorsque vous vous synchronisez avec un système externe, le nom de votre Hook personnalisé peut être plus technique et utiliser un jargon spécifique à ce système. C’est une bonne chose tant que ça reste clair pour une personne familière avec ce système : +Lorsque vous vous synchronisez avec un système extérieur, le nom de votre Hook personnalisé peut être plus technique et utiliser un jargon spécifique à ce système. C’est une bonne chose tant que ça reste clair pour une personne qui a l’habitude de ce système : * ✅ `useMediaQuery(query)` * ✅ `useSocket(url)` * ✅ `useIntersectionObserver(ref, options)` -**Les Hooks personnalisés doivent restés focalisés sur des cas d’usage concrets de haut niveau.** Évitez de créer et d’utiliser de Hooks personnalisé de « cycle de vie » qui agissent comme des alternatives et des enrobages de commodité pour l’API `useEffect` elle-même : +**Les Hooks personnalisés doivent être concentrés sur des cas d’usage concrets de haut niveau.** Évitez de recourir à des Hooks personnalisés de « cycle de vie » qui agissent comme des alternatives et des enrobages de confort pour l’API `useEffect` elle-même : * 🔴 `useMount(fn)` * 🔴 `useEffectOnce(fn)` @@ -1226,9 +1226,9 @@ function useMount(fn) { } ``` -**Les Hooks personnalisés de « cycle de vie » comme `useMount` ne s’intègrent pas bien dans le paradigme de React.** Par exemple, ce code contient une erreur (il ne « réagit » pas aux changements de `roomId` ou `serverUrl`), mais le linter ne vous avertira pas à ce sujet car il ne vérifie que les appels directs à `useEffect`. Il ne connaît rien de votre Hook. +**Les Hooks personnalisés de « cycle de vie » comme `useMount` ne s’intègrent pas bien dans le paradigme de React.** Par exemple, ce code contient une erreur (il ne « réagit » pas aux changements de `roomId` ou `serverUrl`), mais le *linter* ne vous avertira pas à ce sujet car il ne vérifie que les appels directs à `useEffect`. Il ne connaît rien de votre Hook. -Si vous écrivez un effet, commencez par utiliser directement une API de React : +Si vous écrivez un effet, commencez par utiliser directement l’API de React : ```js function ChatRoom({ roomId }) { @@ -1263,13 +1263,13 @@ function ChatRoom({ roomId }) { } ``` -**Un bon Hook personnalisé rend le code appelé plus déclaratif en limitant ce qu’il fait.** Par exemple, `useChatRoom(options)` ne peut que se connecter à un salon de discussion, tandis que `useImpressionLog(eventName, extraData)` ne peut qu’envoyer les journaux à un système d’analytique. Si l’API de votre Hook personnalisé ne limite pas les cas d’usage et est très abstraite, elle risque d’introduire à long terme plus de problèmes qu’elle n’en résoudra. +**Un bon Hook personnalisé rend le code appelé plus déclaratif en limitant ce qu’il fait.** Par exemple, `useChatRoom(options)` ne peut que se connecter à un salon de discussion, tandis que `useImpressionLog(eventName, extraData)` ne peut que contribuer aux journaux analytiques. Si l’API de votre Hook personnalisé ne limite pas les cas d’usage en étant trop abstraite, elle risque d’introduire à long terme plus de problèmes qu’elle n’en résoudra. -### Les Hooks personnalisés vous aident à migrer vers de meilleurs modèles {/*custom-hooks-help-you-migrate-to-better-patterns*/} +### Les Hooks personnalisés vous aident à migrer vers de meilleures approches {/*custom-hooks-help-you-migrate-to-better-patterns*/} -Les effets sont un [« échappatoire »](/learn/escape-hatches) : vous les utilisez quand vous avez besoin de « sortir » de React et quand il n’y a pas de meilleure solution intégrée pour votre cas d’usage. Avec le temps, le but de l’équipe de React est de réduire au maximum le nombre d’effets dans votre app en fournissant des solutions plus spécifiques à des problèmes plus spécifiques. Enrober vos effets dans des Hooks personnalisés facilite la mise à jour de votre code lorsque ces solutions deviennent disponibles. +Les Effets sont une [« échappatoire »](/learn/escape-hatches) : vous les utilisez quand vous avez besoin de « sortir » de React et quand il n’y a pas de meilleure solution intégrée pour votre cas d’usage. Avec le temps, le but de l’équipe de React est de réduire au maximum le nombre d’Effets dans votre appli en fournissant des solutions plus dédiées à des problèmes plus spécifiques. Enrober vos Effets dans des Hooks personnalisés facilite la mise à jour de votre code lorsque ces solutions deviendront disponibles. Revenons à cet exemple : @@ -1332,9 +1332,9 @@ export function useOnlineStatus() { -Dans l’exemple ci-dessus, `useOnlineStatus` est implémentée avec une paire de [`useState`](/reference/react/useState) et [`useEffect`.](/reference/react/useEffect) Cependant, ce n’est pas la meilleure solution possible. Elle ne tient pas compte d’un certain nombre de cas limites. Par exemple, elle suppose que lorsque le composant est monté, `isOnline` est déjà à `true`, mais ça peut être faux si le réseau a déjà été mis hors ligne. Vous pouvez utiliser l’API du navigateur [`navigator.onLine`](https://developer.mozilla.org/fr/docs/Web/API/Navigator/onLine) pour vérifier ça, mais l’utiliser directement ne marchera pas sur le serveur pour générer le HTML initial. En bref, ce code peut être amélioré. +Dans l’exemple ci-dessus, `useOnlineStatus` est implémenté avec le duo [`useState`](/reference/react/useState) et [`useEffect`](/reference/react/useEffect). Cependant, ce n’est pas la meilleure solution possible. Elle ne tient pas compte d’un certain nombre de cas limites. Par exemple, elle suppose que lorsque le composant est monté, `isOnline` est déjà à `true`, mais ça peut être faux si le réseau est d’entrée de jeu hors-ligne. Vous pouvez utiliser l’API du navigateur [`navigator.onLine`](https://developer.mozilla.org/fr/docs/Web/API/Navigator/onLine) pour vérifier ça, mais l’utiliser directement ne marchera pas sur le serveur pour générer le HTML initial. En bref, ce code peut être amélioré. -Heureusement, React 18 inclut une API dédiée appelée [`useSyncExternalStore`](/reference/react/useSyncExternalStore) qui se charge de tous ces problèmes pour vous. Voici comment votre Hook personnalisé `useOnlineStatus` est réécrit pour tirer avantage de cette nouvelle API : +Heureusement, React 18 inclut une API dédiée appelée [`useSyncExternalStore`](/reference/react/useSyncExternalStore) qui se charge de tous ces problèmes pour vous. Voici votre Hook personnalisé `useOnlineStatus` réécrit pour en tirer avantage : @@ -1394,7 +1394,7 @@ export function useOnlineStatus() { -Remarquez comment vous **n’avez pas eu besoin de modifier les composants** pour faire cette migration : +Remarquez que vous **n’avez pas eu besoin de modifier les composants** pour faire cette migration : ```js {2,7} function StatusBar() { @@ -1414,13 +1414,13 @@ C’est une raison pour laquelle il est souvent utile d’enrober des effets dan 2. Vous permettez à vos composants de se concentrer sur l’intention plutôt que sur l’implémentation exacte de vos effets. 3. Lorsque React ajoute de nouvelles fonctionnalités, vous pouvez retirer ces effets sans changer aucun de vos composants. -À la manière d’un [système de design,](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969) vous pourriez trouver utile de commencer à extraire les idiomes communs des composants de votre app dans des Hooks personnalisés. Ainsi, le code de vos composants restera centré sur l’intention et vous éviterez la plupart du temps d’utiliser des effets bruts. De nombreux Hooks personnalisés de qualité sont maintenus par la communauté de React. +À la manière d’un [système de design](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969), vous pourriez trouver utile de commencer à extraire les idiomes communs des composants de votre appli dans des Hooks personnalisés. Ainsi, le code de vos composants restera centré sur l’intention et vous éviterez la plupart du temps d’utiliser des effets bruts. De nombreux Hooks personnalisés de qualité sont maintenus par la communauté React. -#### React fournira-t-il une solution intégrée pour la récupération des données ? {/*will-react-provide-any-built-in-solution-for-data-fetching*/} +#### React fournira-t-il une solution intégrée pour le chargement de données ? {/*will-react-provide-any-built-in-solution-for-data-fetching*/} -Nous travaillons encore sur les détails, mais nous pensons qu’à l’avenir, vous écrirez la récupération de données de cette façon : +Nous travaillons encore sur les détails, mais nous pensons qu’à l’avenir, vous pourrez charger des données comme ceci : ```js {1,4,6} import { use } from 'react'; // Pas encore disponible ! @@ -1432,13 +1432,13 @@ function ShippingForm({ country }) { // ... ``` -Si vous utilisez des Hooks personnalisés comme le `useData` plus haut dans votre app, la migration vers l’approche finalement recommandée nécessitera moins de changements que si vous écrivez manuellement des effets bruts dans chaque composant. Cependant, l’ancienne approche continuera de bien fonctionner, donc si vous vous sentez à l’aise en écrivant des effets bruts, vous pouvez continuer ainsi. +Si vous utilisez des Hooks personnalisés comme le `useData` vu plus haut dans votre appli, la migration vers l’approche finalement recommandée nécessitera moins de changements que si vous écrivez manuellement des effets bruts dans chaque composant. Cependant, l’ancienne approche continuera de bien fonctionner, donc si vous vous sentez à l’aise en écrivant des effets bruts, vous pouvez continuer ainsi. ### Il y a plus d’une façon de faire {/*there-is-more-than-one-way-to-do-it*/} -Supposons que vous voulez implémenter une animation de fondu-enchaîné *en partant de zéro* en utilisant l’API du navigateur [`requestAnimationFrame`](https://developer.mozilla.org/fr/docs/Web/API/window/requestAnimationFrame). Vous pouvez commencer par un effet qui initialise une boucle d’animation. Pendant chaque image de l’animation, vous pourriez changer l’opacité du nœud du DOM si vous le [conservez dans une ref](/learn/manipulating-the-dom-with-refs) jusqu’à ce qu’elle atteigne `1`. Votre code pourrait commencer ainsi : +Supposons que vous souhaitiez implémenter une animation de fondu enchaîné *en partant de zéro* avec l’API [`requestAnimationFrame`](https://developer.mozilla.org/fr/docs/Web/API/window/requestAnimationFrame) du navigateur. Vous pouvez commencer par un Effet qui initialise une boucle d’animation. À chaque étape de l’animation, vous pourriez changer l’opacité du nœud du DOM que vous aurez [conservé dans une ref](/learn/manipulating-the-dom-with-refs) jusqu’à ce qu’elle atteigne `1`. Votre code pourrait commencer ainsi : @@ -1460,7 +1460,7 @@ function Welcome() { const progress = Math.min(timePassed / duration, 1); onProgress(progress); if (progress < 1) { - // Nous avons encore des frames à dessiner. + // Nous avons encore des étapes à dessiner. frameId = requestAnimationFrame(onFrame); } } @@ -1521,7 +1521,7 @@ html, body { min-height: 300px; } -Pour rendre le composant plus lisible, vous pouvez extraire la logique dans un Hook personnalisé `useFadeIn` : +Pour rendre le composant plus lisible, vous pourriez extraire la logique dans un Hook personnalisé `useFadeIn` : @@ -1570,7 +1570,7 @@ export function useFadeIn(ref, duration) { const progress = Math.min(timePassed / duration, 1); onProgress(progress); if (progress < 1) { - // Nous avons encore des frames à dessiner. + // Nous avons encore des étapes à dessiner. frameId = requestAnimationFrame(onFrame); } } @@ -1612,7 +1612,7 @@ html, body { min-height: 300px; } -Vous pouvez conserver le code de `useFadeIn` ainsi, mais vous pouvez le remanier encore. Par exemple, vous pourriez extraire la logique de mise en place de la boucle d’animation de `useFadeIn` dans un Hook personnalisé `useAnimationLoop` : +Vous pouvez conserver le code de `useFadeIn` tel quel, mais vous pouvez le remanier plus avant. Par exemple, vous pourriez extraire la logique de mise en place de la boucle d’animation de `useFadeIn` dans un Hook personnalisé `useAnimationLoop` : @@ -1783,7 +1783,7 @@ export class FadeInAnimation { if (progress === 1) { this.stop(); } else { - // Nous avons encore des frames à dessiner. + // Nous avons encore des étapes à dessiner. this.frameId = requestAnimationFrame(() => this.onFrame()); } } @@ -1814,9 +1814,9 @@ html, body { min-height: 300px; } -Les effets permettent à React de se connecter à des systèmes externes. Plus la coordination entre les effets est nécessaire (par exemple pour enchaîner des animations multiples), plus il est sensé d’extraire *complètement* cette logique des effets et des Hooks, comme dans le bac à sable ci-dessus. Le code extrait *devient* ainsi le « système externe ». Ça permet à vos effets de rester simples car ils n’auront qu’à envoyer des messages au système que vous avez sorti de React. +Les Effets permettent à React de se connecter à des systèmes extérieurs. Plus la coordination entre les Effets est nécessaire (par exemple pour enchaîner des animations multiples), plus il devient pertinent de sortir *complètement* cette logique des Effets et des Hooks, comme dans le bac à sable ci-dessus. Le code extrait *devient* alors le « système extérieur ». Ça permet à vos Effets de rester simples car ils n’auront qu’à envoyer des messages au système que vous avez sorti de React. -Les exemples ci-dessus supposent que la logique de fondu-enchaîné soit écrite en JavaScript. Cependant, cette animation particulière de fondu-enchaîné est à la fois plus simple et beaucoup plus efficace lorsqu’elle est écrite par une simple [animation CSS :](https://developer.mozilla.org/fr/docs/Web/CSS/CSS_Animations/Using_CSS_animations) +Les exemples ci-dessus supposent que la logique de fondu enchaîné soit écrite en JavaScript. Cependant, cette animation particulière est à la fois plus simple et beaucoup plus efficace lorsqu’elle est implémentée comme une simple [animation CSS](https://developer.mozilla.org/fr/docs/Web/CSS/CSS_Animations/Using_CSS_animations) : @@ -1876,12 +1876,12 @@ Parfois, vous n’avez même pas besoin d’un Hook ! - Les Hooks personnalisés vous permettent de partager la logique entre les composants. -- Le nom des Hooks personnalisés doit commencer par `use` et être suivi d’une lettre majuscule. +- Le nom des Hooks personnalisés doit commencer par `use` suivi d’une majuscule. - Les Hooks personnalisés ne partagent que la logique d’état et non l’état lui-même. - Vous pouvez passer des valeurs réactives d’un Hook à un autre, et elles restent à jour. -- Tous les Hooks sont réexécutés à chaque réaffichage de votre composant. +- Tous les Hooks sont réexécutés à chaque rendu de votre composant. - Le code de vos Hooks personnalisés doit être pur, comme le code de votre composant. -- Enrobez les gestionnaires d’événements reçus par les Hooks personnalisés dans des événéments d’effet. +- Enrobez les gestionnaires d’événements reçus par les Hooks personnalisés dans des Événéments d’Effet. - Ne créez pas des Hooks personnalisés comme `useMount`. Veillez à ce que leur objectif soit spécifique. - C’est à vous de décider comment et où définir les frontières de votre code. @@ -1963,7 +1963,7 @@ Remarquez que `App.js` n’a plus besoin d’importer `useState` ni `useEffect` #### Rendez le délai du compteur configurable {/*make-the-counter-delay-configurable*/} -Dans cet exemple, il y a une variable d’état `delay` qui est contrôlée par un curseur, mais sa valeur n’est pas utilisée. Passez la valeur de `delay` à votre Hook personnalisé, et changez le Hook `useCounter` pour utiliser le `delay` passé plutôt que les `1000` ms codés en dur. +Dans cet exemple, il y a une variable d’état `delay` qui est contrôlée par un curseur, mais sa valeur n’est pas utilisée. Passez la valeur de `delay` à votre Hook personnalisé, et changez le Hook `useCounter` pour utiliser le `delay` passé plutôt que les `1000` ms codées en dur. @@ -2065,7 +2065,7 @@ export function useCounter(delay) { #### Extrayez `useInterval` de `useCounter` {/*extract-useinterval-out-of-usecounter*/} -Pour le moment, votre Hook `useCounter` fait de deux choses. Il définit un intervalle et incrémente une variable d’état à chaque tick de l’intervalle. Séparez la logique qui définit l’intervalle dans un Hook séparé appelé `useInterval`. Il devra prendre deux paramètres : la fonction de rappel `onTick` et le `delay`. Après ce changement, votre implémentation de `useCounter` devrait ressembler à ceci : +Pour le moment, votre Hook `useCounter` fait deux choses. Il définit un intervalle et incrémente une variable d’état à chaque tick de l’intervalle. Séparez la logique qui définit l’intervalle dans un Hook distinct appelé `useInterval`. Il devra prendre deux paramètres : la fonction de rappel `onTick` et le `delay`. Après ce changement, votre implémentation de `useCounter` devrait ressembler à ceci : ```js export function useCounter(delay) { @@ -2157,13 +2157,13 @@ Notez que cette solution pose un petit problème que vous résoudrez au prochain -#### Définissez un intervalle de réinitialisation {/*fix-a-resetting-interval*/} +#### Corrigez la réinitialisation indue de l’intervalle {/*fix-a-resetting-interval*/} Dans cet exemple, il y a *deux* intervalles séparés. Le composant `App` appelle `useCounter`, qui appelle `useInterval` pour mettre à jour le compteur à chaque seconde. Mais le composant `App` appelle *aussi* `useInterval` pour mettre à jour aléatoirement la couleur de l’arrière-plan de la page toutes les deux secondes. -Pour certaines raisons, la fonction de rappel qui met à jour l’arrière-plan la page ne s’exécute jamais. Ajoutez quelques journaux dans `useInterval` : +Curieusement, la fonction de rappel qui met à jour l’arrière-plan la page n’est jamais exécutée. Ajoutez quelques messages en console dans `useInterval` : ```js {2,5} useEffect(() => { @@ -2176,9 +2176,9 @@ Pour certaines raisons, la fonction de rappel qui met à jour l’arrière-plan }, [onTick, delay]); ``` -Les journaux correspondent-ils à ce que vous attendez ? Si certains effets semblent se resynchroniser inutilement, pouvez-vous imaginer quelle dépendance est à l’origine de ce comportement ? Existe-t-il un moyen de [supprimer cette dépendance](/learn/removing-effect-dependencies) de votre effet ? +Les messages correspondent-ils à vos attentes ? Si certains Effets semblent se resynchroniser inutilement, pouvez-vous deviner quelle dépendance est à l’origine de ce comportement ? Existe-t-il un moyen de [supprimer cette dépendance](/learn/removing-effect-dependencies) de votre Effet ? -Une fois le problème résolu, vous devriez vous attendre à ce que l’arrière-plan de la page se mette à jour toutes les deux secondes. +Une fois le problème résolu, l’arrière-plan de la page devrait se mettre à jour toutes les deux secondes. @@ -2251,7 +2251,7 @@ export function useInterval(onTick, delay) { -À l’intérieur de `useInterval`, enrobez la fonction de rappel du tick dans un événement d’effet, comme vous l’avez fait [plus tôt dans cette page.](/learn/reusing-logic-with-custom-hooks#passing-event-handlers-to-custom-hooks) +À l’intérieur de `useInterval`, enrobez la fonction de rappel du tick dans un Événement d’Effet, comme vous l’avez fait [plus tôt dans cette page](/learn/reusing-logic-with-custom-hooks#passing-event-handlers-to-custom-hooks). Ça vous permettra d’omettre `onTick` des dépendances de votre effet. L’effet ne se resynchronisera pas à chaque réaffichage du composant de sorte que l’intervalle de changement de la couleur de l’arrière-plan ne sera pas réinitalisé toutes les secondes avant d’avoir la possibilité de se déclencher. @@ -2322,21 +2322,21 @@ export function useInterval(callback, delay) { -#### Implémentez un mouvement d’échelonnement {/*implement-a-staggering-movement*/} +#### Implémentez un mouvement échelonné {/*implement-a-staggering-movement*/} Dans cet exemple, le Hook `usePointerPosition()` suit la position du curseur. Essayez de déplacer votre curseur ou votre doigt dans la zone de prévisualisation et voyez le point rouge suivre votre mouvement. Sa position est enregistrée dans la variable `pos1`. -En réalité, il y a cinq (!) points rouges différents qui sont dessinés. Vous ne les voyez pas actuellement car ils apparaissent tous sur la même position. C’est ça que vous devez corriger. Ce que vous voulez implémenter à la place d’un mouvement « décalé » : chaque point doit « suivre » la trajectoire du point précédent. Par exemple, si vous déplacez rapidement votre curseur, le premier point doit le suivre immédiatement, le deuxième point doit suivre le premier point avec un léger décalage, le troisième point doit suivre le deuxième point, et ainsi de suite. +En réalité, il y a cinq (!) points rouges différents qui sont dessinés. Vous ne les voyez pas pour le moment car ils apparaissent tous à la même position. C’est ça que vous devez corriger. Vous devez implémenter au lieu de ça un mouvement « échelonné » : chaque point doit « suivre » la trajectoire du point précédent. Par exemple, si vous déplacez rapidement votre curseur, le premier point doit le suivre immédiatement, le deuxième point doit suivre le premier point avec un léger décalage, le troisième point doit suivre le deuxième point, et ainsi de suite. -Vous devez implémenter le Hook personnalisé `useDelayedValue`. Son implémentation actuelle retourne la `value` qui lui a été donnée. À la place, vous voulez retourner la valeur qu’elle valait `delay` millisecondes plus tôt. Vous aurez peut-être besoin d’un état et d’un effet pour ça. +Vous devez implémenter le Hook personnalisé `useDelayedValue`. Son implémentation actuelle retourne la `value` qui lui a été donnée. Au lieu de ça, vous souhaitez retourner la valeur qu’elle avait `delay` millisecondes plus tôt. Vous aurez peut-être besoin d’un état et d’un Effet pour y arriver. Une fois que vous aurez implémenté `useDelayedValue`, vous devriez voir les points se déplacer les uns après les autres. -Vous aurez besoin de stocker `delayedValue` comme variable d’état à l’intérieur de votre Hook. Quand la `value` change, vous devrez exécuter un effet. Cet effet devra mettre à jour `delayedValue` après le `delay`. Vous pouvez trouver utile d’appeler `setTimeout`. +Vous aurez besoin de stocker `delayedValue` comme variable d’état à l’intérieur de votre Hook. Quand la `value` change, vous devrez exécuter un Effet. Cet Effet devra mettre à jour `delayedValue` après le `delay`. Recourir à `setTimeout` pourrait s’avérer utile. -Est-ce que cet effet a besoin de nettoyage ? Pourquoi ou pourquoi pas ? +Est-ce que cet effet a besoin de nettoyage ? Pourquoi (ou pourquoi non) ? @@ -2486,7 +2486,7 @@ body { min-height: 300px; } -Notez que cet effet n’a *pas besoin* de nettoyage. Si vous appelez `clearTimeout` dans la fonction de nettoyage, alors à chaque changement de `value`, ça réinitialisera le compte à rebours déjà programmé. Pour garder le mouvement continu, il faut que tous les comptes à rebours soient déclenchés. +Notez que cet effet n’a *pas besoin* de nettoyage. Si vous appelez `clearTimeout` dans la fonction de nettoyage, alors à chaque changement de `value`, ça réinitialisera le compte à rebours déjà programmé. Pour obtenir un mouvement continu, il faut que tous les comptes à rebours arrivent à leur terme et déclenchent leur mise à jour associée. From 7ca42dfff8e50d78893bb2fec412c3d1ac42949d Mon Sep 17 00:00:00 2001 From: Romain Linsolas Date: Tue, 16 May 2023 20:09:10 +0200 Subject: [PATCH 4/5] Use "Design System" without translation --- src/content/learn/reusing-logic-with-custom-hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/learn/reusing-logic-with-custom-hooks.md b/src/content/learn/reusing-logic-with-custom-hooks.md index 323a88cfc..ac91c5068 100644 --- a/src/content/learn/reusing-logic-with-custom-hooks.md +++ b/src/content/learn/reusing-logic-with-custom-hooks.md @@ -1414,7 +1414,7 @@ C’est une raison pour laquelle il est souvent utile d’enrober des effets dan 2. Vous permettez à vos composants de se concentrer sur l’intention plutôt que sur l’implémentation exacte de vos effets. 3. Lorsque React ajoute de nouvelles fonctionnalités, vous pouvez retirer ces effets sans changer aucun de vos composants. -À la manière d’un [système de design](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969), vous pourriez trouver utile de commencer à extraire les idiomes communs des composants de votre appli dans des Hooks personnalisés. Ainsi, le code de vos composants restera centré sur l’intention et vous éviterez la plupart du temps d’utiliser des effets bruts. De nombreux Hooks personnalisés de qualité sont maintenus par la communauté React. +À la manière d’un [Design System](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969), vous pourriez trouver utile de commencer à extraire les idiomes communs des composants de votre appli dans des Hooks personnalisés. Ainsi, le code de vos composants restera centré sur l’intention et vous éviterez la plupart du temps d’utiliser des effets bruts. De nombreux Hooks personnalisés de qualité sont maintenus par la communauté React. From a64fbd04fd1507e6b40eb9bcd4d05c76199fe58e Mon Sep 17 00:00:00 2001 From: Christophe Porteneuve Date: Wed, 17 May 2023 11:50:22 +0200 Subject: [PATCH 5/5] copy(hooks): final review pass on custom hooks --- src/content/learn/reusing-logic-with-custom-hooks.md | 12 ++++++------ src/sidebarLearn.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/content/learn/reusing-logic-with-custom-hooks.md b/src/content/learn/reusing-logic-with-custom-hooks.md index ac91c5068..f9496cd52 100644 --- a/src/content/learn/reusing-logic-with-custom-hooks.md +++ b/src/content/learn/reusing-logic-with-custom-hooks.md @@ -252,7 +252,7 @@ function getSorted(items) { } ``` -Ça garantit que votre code peut appeler cette fonction normale n’importe où, y compris dans des conditions : +Ça garantit que votre code peut appeler cette fonction n’importe où, y compris dans des conditions : ```js function List({ items, shouldSort }) { @@ -265,7 +265,7 @@ function List({ items, shouldSort }) { } ``` -Vous devez utiliser le préfixe `use` pour une fonction (et ainsi, en faire un Hook) si elle utilise elle-même un Hook : +Vous devez utiliser le préfixe `use` pour une fonction (et ainsi, en faire un Hook) si elle utilise elle-même un Hook dans son code : ```js // ✅ Correct : un Hook qui utilise un autre Hook @@ -274,7 +274,7 @@ function useAuth() { } ``` -Techniquement, cette règle n’est pas vérifiée par React. En principe, vous pouvez créer un Hook qui n’appelle pas d’autres Hooks. C’est souvent déroutant et limitant, aussi est-il préférable d’éviter cette approche. Cependant, il peut y avoir de rares cas où c’est utile. Par exemple, votre fonction n’appelle pas encore de Hook, mais vous prévoyez d’y ajouter des appels à des Hooks à l'avenir. Il est alors logique d’utiliser le préfixe `use` : +Techniquement, cette règle n’est pas vérifiée par React. En principe, vous pouvez créer un Hook qui n’appelle pas d’autres Hooks. C’est souvent déroutant et limitant, aussi est-il préférable d’éviter cette approche. Cependant, il peut y avoir de rares cas où c’est utile. Par exemple, votre fonction n’appelle pas encore de Hook, mais vous prévoyez d’y ajouter des appels à des Hooks à l’avenir. Il est alors logique d’utiliser le préfixe `use` : ```js {3-4} @@ -646,7 +646,7 @@ export default function ChatRoom({ roomId }) { C’est plus simple ainsi ! (Mais ça fait toujours la même chose.) -Remarquez que la logique *réagit toujours* aux changement des props et de l’état. Essayez de modifier l’URL du serveur ou le salon choisi : +Remarquez que la logique *réagit toujours* aux changement des props et de l’état. Essayez de modifier l’URL du serveur ou le salon choisi : @@ -1412,7 +1412,7 @@ C’est une raison pour laquelle il est souvent utile d’enrober des effets dan 1. Vous rendez le flux de données vers et depuis vos effets très explicite. 2. Vous permettez à vos composants de se concentrer sur l’intention plutôt que sur l’implémentation exacte de vos effets. -3. Lorsque React ajoute de nouvelles fonctionnalités, vous pouvez retirer ces effets sans changer aucun de vos composants. +3. Lorsque React ajoute de nouvelles fonctionnalités, vous pouvez retirer ces effets sans changer le code d’aucun de vos composants. À la manière d’un [Design System](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969), vous pourriez trouver utile de commencer à extraire les idiomes communs des composants de votre appli dans des Hooks personnalisés. Ainsi, le code de vos composants restera centré sur l’intention et vous éviterez la plupart du temps d’utiliser des effets bruts. De nombreux Hooks personnalisés de qualité sont maintenus par la communauté React. @@ -2409,7 +2409,7 @@ body { min-height: 300px; } -Voici une version fonctionnelle. Vous conservez `delayedValue` comme variable d’état. Quand `value` change, votre effet planifie un compte à rebours pour mettre à jour `delayedValue`. C’est pourquoi `delayedValue` est toujours « en retard » sur `value`. +Voici une version fonctionnelle. Vous conservez `delayedValue` comme variable d’état. Quand `value` change, votre effet planifie un compte à rebours pour mettre à jour `delayedValue`. C’est pourquoi `delayedValue` est toujours « en retard » par rapport à `value`. diff --git a/src/sidebarLearn.json b/src/sidebarLearn.json index 1cdaac26e..4bee3e6c3 100644 --- a/src/sidebarLearn.json +++ b/src/sidebarLearn.json @@ -189,7 +189,7 @@ "path": "/learn/removing-effect-dependencies" }, { - "title": "Reusing Logic with Custom Hooks", + "title": "Réutiliser de la logique grâce aux Hooks personnalisés", "path": "/learn/reusing-logic-with-custom-hooks" } ]