diff --git a/src/content/reference/react/useEffect.md b/src/content/reference/react/useEffect.md index 8d04f205c..ab639d571 100644 --- a/src/content/reference/react/useEffect.md +++ b/src/content/reference/react/useEffect.md @@ -4,7 +4,7 @@ title: useEffect -`useEffect` is a React Hook that lets you [synchronize a component with an external system.](/learn/synchronizing-with-effects) +`useEffect` est un Hook React qui vous permet de [synchroniser un composant React avec un système extérieur](/learn/synchronizing-with-effects). ```js useEffect(setup, dependencies?) @@ -16,11 +16,11 @@ useEffect(setup, dependencies?) --- -## Reference {/*reference*/} +## Référence {/*reference*/} ### `useEffect(setup, dependencies?)` {/*useeffect*/} -Call `useEffect` at the top level of your component to declare an Effect: +Appelez `useEffect` à la racine de votre composant pour déclarer un Effet : ```js import { useEffect } from 'react'; @@ -40,43 +40,43 @@ function ChatRoom({ roomId }) { } ``` -[See more examples below.](#usage) +[Voir d'autres exemples ci-dessous](#usage). -#### Parameters {/*parameters*/} +#### Paramètres {/*parameters*/} -* `setup`: The function with your Effect's logic. Your setup function may also optionally return a *cleanup* function. When your component is added to the DOM, React will run your setup function. After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values. After your component is removed from the DOM, React will run your cleanup function. - -* **optional** `dependencies`: The list of all reactive values referenced inside of the `setup` code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is [configured for React](/learn/editor-setup#linting), it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like `[dep1, dep2, dep3]`. React will compare each dependency with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. If you omit this argument, your Effect will re-run after every re-render of the component. [See the difference between passing an array of dependencies, an empty array, and no dependencies at all.](#examples-dependencies) +* `setup` : la fonction contenant la logique de votre Effet. Votre fonction de mise en place peut par ailleurs renvoyer une fonction de *nettoyage*. Quand votre composant sera ajouté au DOM, React exécutera votre fonction de mise en place. Après chaque nouveau rendu dont les dépendances ont changé, React commencera par exécuter votre fonction de nettoyage (si vous en avez fourni une) avec les anciennes valeurs, puis exécutera votre fonction de mise en place avec les nouvelles valeurs. Une fois votre composant retiré du DOM, React exécutera votre fonction de nettoyage une dernière fois. -#### Returns {/*returns*/} +* `dependencies` **optionnelles** : la liste des valeurs réactives référencées par le code de `setup`. Les valeurs réactives comprennent les props, les variables d'état et toutes les variables et fonctions déclarées localement dans le corps de votre composant. Si votre *linter* est [configuré pour React](/learn/editor-setup#linting), il vérifiera que chaque valeur réactive concernée est bien spécifiée comme dépendance. La liste des dépendances doit avoir un nombre constant d'éléments et utiliser un littéral défini à la volée, du genre `[dep1, dep2, dep3]`. React comparera chaque dépendance à sa valeur précédente au moyen de la comparaison [`Object.is`](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Si vous omettez cet argument, votre Effet sera re-exécuté après chaque rendu du composant. [Découvrez la différence entre passer un tableau de dépendances, un tableau vide ou aucun tableau](#examples-dependencies). -`useEffect` returns `undefined`. +#### Valeur renvoyée {/*returns*/} -#### Caveats {/*caveats*/} +`useEffect` renvoie `undefined`. -* `useEffect` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it. +#### Limitations {/*caveats*/} -* If you're **not trying to synchronize with some external system,** [you probably don't need an Effect.](/learn/you-might-not-need-an-effect) +* `useEffect` est un Hook, vous pouvez donc uniquement l’appeler **à la racine de votre composant** ou de vos propres Hooks. Vous ne pouvez pas l’appeler à l’intérieur de boucles ou de conditions. Si nécessaire, extrayez un nouveau composant et déplacez l'état dans celui-ci. -* When Strict Mode is on, React will **run one extra development-only setup+cleanup cycle** before the first real setup. This is a stress-test that ensures that your cleanup logic "mirrors" your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, [implement the cleanup function.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) +* Si vous **ne cherchez pas à synchroniser avec un système extérieur**, c'est que [vous n'avez probablement pas besoin d'un Effet](/learn/you-might-not-need-an-effect). -* If some of your dependencies are objects or functions defined inside the component, there is a risk that they will **cause the Effect to re-run more often than needed.** To fix this, remove unnecessary [object](#removing-unnecessary-object-dependencies) and [function](#removing-unnecessary-function-dependencies) dependencies. You can also [extract state updates](#updating-state-based-on-previous-state-from-an-effect) and [non-reactive logic](#reading-the-latest-props-and-state-from-an-effect) outside of your Effect. +* Quand le mode strict est activé, React **appellera une fois de plus votre cycle mise en place + nettoyage, uniquement en développement**, avant la première mise en place réelle. C'est une mise à l'épreuve pour vérifier que votre logique de nettoyage reflète bien votre logique de mise en place, et décommissionne ou défait toute la mise en place effectuée. Si ça entraîne des problèmes, [écrivez une fonction de nettoyage](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development). -* If your Effect wasn't caused by an interaction (like a click), React will let the browser **paint the updated screen first before running your Effect.** If your Effect is doing something visual (for example, positioning a tooltip), and the delay is noticeable (for example, it flickers), replace `useEffect` with [`useLayoutEffect`.](/reference/react/useLayoutEffect) +* Si certaines de vos dépendances sont des objets ou fonctions définies au sein de votre composant, il existe un risque qu'elles **entraînent des exécutions superflues de votre Effet**. Pour corriger ça, retirez les dépendances superflues sur des [objets](#removing-unnecessary-object-dependencies) et [fonctions](#removing-unnecessary-function-dependencies). Vous pouvez aussi [extraire les mises à jour d'état](#updating-state-based-on-previous-state-from-an-effect) et la [logique non réactive](#reading-the-latest-props-and-state-from-an-effect) hors de votre Effet. -* Even if your Effect was caused by an interaction (like a click), **the browser may repaint the screen before processing the state updates inside your Effect.** Usually, that's what you want. However, if you must block the browser from repainting the screen, you need to replace `useEffect` with [`useLayoutEffect`.](/reference/react/useLayoutEffect) +* Si votre Effet ne découlait pas d'une interaction (telle qu'un clic), React laissera le navigateur **rafraîchir l'affichage à l'écran avant d'exécuter votre Effet**. Si votre Effet a des aspects visuels (par exemple, il positionne une infobulle) et que le retard est perceptible (par exemple, l'affichage vacille), remplacez `useEffect` par [`useLayoutEffect`](/reference/react/useLayoutEffect). -* Effects **only run on the client.** They don't run during server rendering. +* Même si votre Effet est déclenché par une interaction (telle qu'un clic), **le navigateur est susceptible de rafraîchir l'affichage avant d'avoir traité les mises à jour d'état au sein de votre Effet**. C'est généralement ce que vous souhaitez. Cependant, si vous devez empêcher le navigateur de rafraîchir l'affichage tout de suite, remplacez `useEffect` par [`useLayoutEffect`](/reference/react/useLayoutEffect). + +* Les Effets **ne sont exécutés que côté client**. Ils sont ignorés lors du rendu côté serveur. --- -## Usage {/*usage*/} +## Utilisation {/*usage*/} -### Connecting to an external system {/*connecting-to-an-external-system*/} +### Se connecter à un système extérieur {/*connecting-to-an-external-system*/} -Some components need to stay connected to the network, some browser API, or a third-party library, while they are displayed on the page. These systems aren't controlled by React, so they are called *external.* +Certains composants ont besoin de rester connectés au réseau, ou à des API du navigateur, ou à des bibliothèques tierces, tout le temps qu'ils sont à l'écran. Ces systèmes ne sont pas gérés par React, on les qualifie donc de systèmes *extérieurs*. -To [connect your component to some external system,](/learn/synchronizing-with-effects) call `useEffect` at the top level of your component: +Afin de [connecter votre composant à un système extérieur](/learn/synchronizing-with-effects), appelez `useEffect` au niveau racine de votre fonction composant : ```js [[1, 8, "const connection = createConnection(serverUrl, roomId);"], [1, 9, "connection.connect();"], [2, 11, "connection.disconnect();"], [3, 13, "[serverUrl, roomId]"]] import { useEffect } from 'react'; @@ -86,55 +86,55 @@ function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); useEffect(() => { - const connection = createConnection(serverUrl, roomId); + const connection = createConnection(serverUrl, roomId); connection.connect(); - return () => { + return () => { connection.disconnect(); - }; + }; }, [serverUrl, roomId]); // ... } ``` -You need to pass two arguments to `useEffect`: +Vous devez passer deux arguments à `useEffect` : -1. A *setup function* with setup code that connects to that system. - - It should return a *cleanup function* with cleanup code that disconnects from that system. -2. A list of dependencies including every value from your component used inside of those functions. +1. Une *fonction de mise en place* avec du code de mise en place qui vous connecte au système. + - Elle devrait renvoyer une *fonction de nettoyage* avec du code de nettoyage qui vous déconnecte du système. +2. Une liste de dépendances comprenant chaque valeur issue de votre composant que ces fonctions utilisent. -**React calls your setup and cleanup functions whenever it's necessary, which may happen multiple times:** +**React appellera vos fonctions de mise en place et de nettoyage chaque fois que nécessaire, ce qui peut survenir plusieurs fois :** -1. Your setup code runs when your component is added to the page *(mounts)*. -2. After every re-render of your component where the dependencies have changed: - - First, your cleanup code runs with the old props and state. - - Then, your setup code runs with the new props and state. -3. Your cleanup code runs one final time after your component is removed from the page *(unmounts).* +1. Votre code de mise en place est exécuté quand votre composant est ajouté à la page *(montage)*. +2. Après chaque nouveau rendu de votre composant, si les dépendances ont changé : + - D'abord, votre code de nettoyage est exécuté avec les anciennes props et valeurs d'états. + - Ensuite, votre code de mise en place est exécuté avec les nouvelles props et valeurs d'états. +3. Votre code de nettoyage est exécuté une dernière fois lorsque votre composant est retiré de l'arborescence de la page *(démontage)*. -**Let's illustrate this sequence for the example above.** +**Illustrons cette séquence pour l'exemple précédent.** -When the `ChatRoom` component above gets added to the page, it will connect to the chat room with the initial `serverUrl` and `roomId`. If either `serverUrl` or `roomId` change as a result of a re-render (say, if the user picks a different chat room in a dropdown), your Effect will *disconnect from the previous room, and connect to the next one.* When the `ChatRoom` component is removed from the page, your Effect will disconnect one last time. +Lorsque le composant `ChatRoom` ci-dessus sera ajouté à la page, il se connectera au salon de discussion en utilisant les valeurs initiales de `serverUrl` et `roomId`. Si l'une ou l'autre de ces deux valeurs change suite à un nouveau rendu (peut-être l'utilisateur a-t-il choisi un autre salon dans la liste déroulante), votre Effet *se déconnectera du salon précédent, puis se connectera au nouveau salon*. Lorsque le composant `ChatRoom` sera retiré de la page, votre Effet se déconnectera une dernière fois. -**To [help you find bugs,](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) in development React runs setup and cleanup one extra time before the setup.** This is a stress-test that verifies your Effect's logic is implemented correctly. If this causes visible issues, your cleanup function is missing some logic. The cleanup function should stop or undo whatever the setup function was doing. The rule of thumb is that the user shouldn't be able to distinguish between the setup being called once (as in production) and a *setup* → *cleanup* → *setup* sequence (as in development). [See common solutions.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) +**Pour [vous aider à repérer des bugs](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed), en développement React exécutera un premier cycle de mise en place et de nettoyage, avant d'exécuter la mise en place nominale.** C'est une mise à l'épreuve pour vérifier que la logique de votre Effet est implémentée correctement. Si ça entraîne des problèmes, c'est que votre code de nettoyage est manquant ou incomplet. La fonction de nettoyage devrait arrêter ou défaire ce que la fonction de mise en place a initié. La règle à suivre est simple : l'utilisateur ne devrait pas pouvoir faire la différence entre une exécution unique de la mise en place (comme en production) et une séquence *mise en place* → *nettoyage* → *mise en place* (comme en développement). [Explorez les solutions courantes](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development). -**Try to [write every Effect as an independent process](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process) and [think about a single setup/cleanup cycle at a time.](/learn/lifecycle-of-reactive-effects#thinking-from-the-effects-perspective)** It shouldn't matter whether your component is mounting, updating, or unmounting. When your cleanup logic correctly "mirrors" the setup logic, your Effect is resilient to running setup and cleanup as often as needed. +**Essayez [d'écrire chaque Effet comme un processus autonome](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process) et de [réfléchir à un seul cycle de mise en place / nettoyage à la fois](/learn/lifecycle-of-reactive-effects#thinking-from-the-effects-perspective).** Le fait que votre composant soit en train d'être monté, de se mettre à jour ou d'être démonté ne devrait avoir aucune importance. Lorsque votre logique de nettoyage reflète correctement celle de mise en place, votre Effet n'a aucun problème avec des exécutions multiples de ses codes de mise en place et de nettoyage. -An Effect lets you [keep your component synchronized](/learn/synchronizing-with-effects) with some external system (like a chat service). Here, *external system* means any piece of code that's not controlled by React, such as: +Un Effet vous permet de [garder votre composant synchronisé](/learn/synchronizing-with-effects) avec un système extérieur (tel qu'un service de discussion). Dans ce contexte, *système extérieur* désigne n'importe quel bout de code qui n'est pas géré par React, par exemple : -* A timer managed with [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) and [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval). -* An event subscription using [`window.addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) and [`window.removeEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener). -* A third-party animation library with an API like `animation.start()` and `animation.reset()`. +* Un timer géré par [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) et [`clearInterval()`](https://developer.mozilla.org/fr/docs/Web/API/clearInterval). +* Un abonnement sur événement grâce à [`window.addEventListener()`](https://developer.mozilla.org/fr/docs/Web/API/EventTarget/addEventListener) et [`window.removeEventListener()`](https://developer.mozilla.org/fr/docs/Web/API/EventTarget/removeEventListener). +* Une bibliothèque d'animations tierce avec une API du genre `animation.start()` et `animation.reset()`. -**If you're not connecting to any external system, [you probably don't need an Effect.](/learn/you-might-not-need-an-effect)** +**Si vous ne vous connectez pas à un système extérieur, [vous n'avez sans doute pas besoin d'un Effet](/learn/you-might-not-need-an-effect).** - + -#### Connecting to a chat server {/*connecting-to-a-chat-server*/} +#### Se connecter à un serveur de discussion {/*connecting-to-a-chat-server*/} -In this example, the `ChatRoom` component uses an Effect to stay connected to an external system defined in `chat.js`. Press "Open chat" to make the `ChatRoom` component appear. This sandbox runs in development mode, so there is an extra connect-and-disconnect cycle, as [explained here.](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) Try changing the `roomId` and `serverUrl` using the dropdown and the input, and see how the Effect re-connects to the chat. Press "Close chat" to see the Effect disconnect one last time. +Dans cet exemple, le composant `ChatRoom` utilise un Effet pour rester connecté à un système extérieur défini dans `chat.js`. Appuyez sur « Ouvrir le salon » pour que le composant `ChatRoom` apparaisse. Ce bac à sable est en mode développement, il y aura donc un cycle supplémentaire de connexion-déconnexion, comme [expliqué ici](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed). Essayez de changer `roomId` et `serverUrl` en utilisant la liste déroulante et le champ de saisie, et voyez comme l'Effet se reconnecte au salon. Appuyez sur « Fermer le salon » pour vous déconnecter une dernière fois. @@ -156,13 +156,13 @@ function ChatRoom({ roomId }) { return ( <> -

Welcome to the {roomId} room!

+

Bienvenue dans le salon {roomId} !

); } @@ -173,18 +173,18 @@ export default function App() { return ( <> {show &&
} {show && } @@ -195,13 +195,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Une véritable implémentation se connecterait en vrai au serveur return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Connexion au salon « ' + roomId + ' » sur ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Déconnexion du salon « ' + roomId + ' » sur ' + serverUrl); } }; } @@ -216,9 +216,9 @@ button { margin-left: 10px; } -#### Listening to a global browser event {/*listening-to-a-global-browser-event*/} +#### Écouter un événement global du navigateur {/*listening-to-a-global-browser-event*/} -In this example, the external system is the browser DOM itself. Normally, you'd specify event listeners with JSX, but you can't listen to the global [`window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object this way. An Effect lets you connect to the `window` object and listen to its events. Listening to the `pointermove` event lets you track the cursor (or finger) position and update the red dot to move with it. +Dans cet exemple, le système extérieur est le DOM navigateur lui-même. En temps normal, vous définiriez des gestionnaires d'événements en JSX, mais vous ne pouvez pas écouter des événements de l'objet global [`window`](https://developer.mozilla.org/fr/docs/Web/API/Window) de cette façon. Un Effet vous permet de vous connecter à l'objet `window` et d'écouter ses événements. En vous abonnant à l'événement `pointermove`, vous pouvez surveiller la position du curseur (ou du doigt) et mettre à jour le point rouge pour bouger avec lui. @@ -265,9 +265,9 @@ body { -#### Triggering an animation {/*triggering-an-animation*/} +#### Déclencher une animation {/*triggering-an-animation*/} -In this example, the external system is the animation library in `animation.js`. It provides a JavaScript class called `FadeInAnimation` that takes a DOM node as an argument and exposes `start()` and `stop()` methods to control the animation. This component [uses a ref](/learn/manipulating-the-dom-with-refs) to access the underlying DOM node. The Effect reads the DOM node from the ref and automatically starts the animation for that node when the component appears. +Dans cet exemple, le système extérieur est la bibliothèque d'animation dans `animation.js`. Elle fournit une classe JavaScript appelée `FadeInAnimation` qui prend un nœud DOM comme argument et expose des méthodes `start()` et `stop()` pour contrôler l'animation. Ce composant [utilise une ref](/learn/manipulating-the-dom-with-refs) pour accéder au nœud DOM sous-jacent. L'Effet lit le nœud DOM depuis la ref et démarre automatiquement l'animation pour ce nœud lorsque le composant apparaît. @@ -298,7 +298,7 @@ function Welcome() { backgroundImage: 'radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%)' }} > - Welcome + Bienvenue ); } @@ -308,7 +308,7 @@ export default function App() { return ( <>
{show && } @@ -325,11 +325,11 @@ export class FadeInAnimation { start(duration) { this.duration = duration; if (this.duration === 0) { - // Jump to end immediately + // Sauter à la fin this.onProgress(1); } else { this.onProgress(0); - // Start animating + // Commencer l'animation this.startTime = performance.now(); this.frameId = requestAnimationFrame(() => this.onFrame()); } @@ -339,7 +339,7 @@ export class FadeInAnimation { const progress = Math.min(timePassed / this.duration, 1); this.onProgress(progress); if (progress < 1) { - // We still have more frames to paint + // Il nous reste des étapes à afficher this.frameId = requestAnimationFrame(() => this.onFrame()); } } @@ -364,9 +364,9 @@ html, body { min-height: 300px; } -#### Controlling a modal dialog {/*controlling-a-modal-dialog*/} +#### Contrôler une boîte de dialogue modale {/*controlling-a-modal-dialog*/} -In this example, the external system is the browser DOM. The `ModalDialog` component renders a [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) element. It uses an Effect to synchronize the `isOpen` prop to the [`showModal()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal) and [`close()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close) method calls. +Dans cet exemple, le système extérieur est le DOM navigateur. Le composant `ModalDialog` utilise un élément [``](https://developer.mozilla.org/fr/docs/Web/HTML/Element/dialog). Il utilise un Effet pour synchroniser la prop `isOpen` avec les appels aux méthodes [`showModal()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal) et [`close()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close). @@ -379,14 +379,14 @@ export default function App() { return ( <> - Hello there! + Salut !
+ }}>Fermer
); @@ -424,9 +424,9 @@ body { -#### Tracking element visibility {/*tracking-element-visibility*/} +#### Surveiller la visibilité d'un élément {/*tracking-element-visibility*/} -In this example, the external system is again the browser DOM. The `App` component displays a long list, then a `Box` component, and then another long list. Scroll the list down. Notice that when the `Box` component appears in the viewport, the background color changes to black. To implement this, the `Box` component uses an Effect to manage an [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). This browser API notifies you when the DOM element is visible in the viewport. +Dans cet exemple, le système extérieur est encore le DOM navigateur. Le composant `App` affiche une longue liste, puis un composant `Box`, et enfin une autre longue liste. Faites défiler la liste vers le bas. Voyez comment, lorsque le composant `Box` apparaît finalement à l'écran, la couleur de fond passe au noir. Pour implémenter ça, le composant `Box` utilise un Effet pour gérer un [`IntersectionObserver`](https://developer.mozilla.org/fr/docs/Web/API/Intersection_Observer_API). Cette API navigateur vous notifie lorsque l'élément DOM est visible dans la zone de rendu de la fenêtre. @@ -448,7 +448,7 @@ export default function App() { function LongSection() { const items = []; for (let i = 0; i < 50; i++) { - items.push(
  • Item #{i} (keep scrolling)
  • ); + items.push(
  • Élément #{i} (continuez à défiler)
  • ); } return
      {items}
    } @@ -500,11 +500,11 @@ export default function Box() { --- -### Wrapping Effects in custom Hooks {/*wrapping-effects-in-custom-hooks*/} +### Enrober vos Effets dans des Hooks personnalisés {/*wrapping-effects-in-custom-hooks*/} -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. If you find yourself often needing to manually write Effects, it's usually a sign that you need to extract some [custom Hooks](/learn/reusing-logic-with-custom-hooks) for common behaviors your components rely on. +Les Effets sont une [« échappatoire »](/learn/escape-hatches) : vous vous en servez pour « sortir de React », et lorsqu'il n'y a pas de meilleure solution disponible pour votre cas de figure. Si vous vous retrouvez à souvent écrire manuellement des Effets, c'est généralement le signe que vous devriez en extraire certains sous forme de [Hooks personnalisés](/learn/reusing-logic-with-custom-hooks) pour les comportements courants dont vous équipez vos composants. -For example, this `useChatRoom` custom Hook "hides" the logic of your Effect behind a more declarative API: +Par exemple, ce Hook personnalisé `useChatRoom` « masque » toute la logique de votre Effet derrière une API plus déclarative. ```js {1,11} function useChatRoom({ serverUrl, roomId }) { @@ -520,7 +520,7 @@ function useChatRoom({ serverUrl, roomId }) { } ``` -Then you can use it from any component like this: +Vous pouvez dès lors l'utiliser dans n'importe quel composant, comme ceci : ```js {4-7} function ChatRoom({ roomId }) { @@ -533,15 +533,15 @@ function ChatRoom({ roomId }) { // ... ``` -There are also many excellent custom Hooks for every purpose available in the React ecosystem. +L'écosystème React propose de nombreux excellents Hooks personnalisés pour tous les besoins. -[Learn more about wrapping Effects in custom Hooks.](/learn/reusing-logic-with-custom-hooks) +[Apprenez à enrober vos Effets dans des Hooks personnalisés](/learn/reusing-logic-with-custom-hooks). - + -#### Custom `useChatRoom` Hook {/*custom-usechatroom-hook*/} +#### Hook `useChatRoom` personnalisé {/*custom-usechatroom-hook*/} -This example is identical to one of the [earlier examples,](#examples-connecting) but the logic is extracted to a custom Hook. +Cet exemple est identique à un des [exemples précédents](#examples-connecting), mais sa logique est extraite dans un Hook personnalisé. @@ -560,13 +560,13 @@ function ChatRoom({ roomId }) { return ( <> -

    Welcome to the {roomId} room!

    +

    Bienvenue dans le salon {roomId} !

    ); } @@ -577,18 +577,18 @@ export default function App() { return ( <> {show &&
    } {show && } @@ -614,13 +614,13 @@ export function useChatRoom({ serverUrl, 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 en vrai au serveur return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Connexion au salon « ' + roomId + ' » sur ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Déconnexion du salon « ' + roomId + ' » sur ' + serverUrl); } }; } @@ -635,9 +635,9 @@ button { margin-left: 10px; } -#### Custom `useWindowListener` Hook {/*custom-usewindowlistener-hook*/} +#### Hook `useWindowListener` personnalisé {/*custom-usewindowlistener-hook*/} -This example is identical to one of the [earlier examples,](#examples-connecting) but the logic is extracted to a custom Hook. +Cet exemple est identique à un des [exemples précédents](#examples-connecting), mais sa logique est extraite dans un Hook personnalisé. @@ -692,9 +692,9 @@ body { -#### Custom `useIntersectionObserver` Hook {/*custom-useintersectionobserver-hook*/} +#### Hook `useIntersectionObserver` personnalisé {/*custom-useintersectionobserver-hook*/} -This example is identical to one of the [earlier examples,](#examples-connecting) but the logic is partially extracted to a custom Hook. +Cet exemple est identique à un des [exemples précédents](#examples-connecting), mais sa logique est extraite dans un Hook personnalisé. @@ -716,7 +716,7 @@ export default function App() { function LongSection() { const items = []; for (let i = 0; i < 50; i++) { - items.push(
  • Item #{i} (keep scrolling)
  • ); + items.push(
  • Élément #{i} (continuez à défiler)
  • ); } return
      {items}
    } @@ -784,11 +784,11 @@ export function useIntersectionObserver(ref) { --- -### Controlling a non-React widget {/*controlling-a-non-react-widget*/} +### Contrôler un widget non géré par React {/*controlling-a-non-react-widget*/} -Sometimes, you want to keep an external system synchronized to some prop or state of your component. +Il peut arriver que vous souhaitiez garder un système extérieur synchronisé avec la valeur d'une prop ou d'un état de votre composant. -For example, if you have a third-party map widget or a video player component written without React, you can use an Effect to call methods on it that make its state match the current state of your React component. This Effect creates an instance of a `MapWidget` class defined in `map-widget.js`. When you change the `zoomLevel` prop of the `Map` component, the Effect calls the `setZoom()` on the class instance to keep it synchronized: +Imaginons par exemple que vous ayez un widget tiers de cartographie, ou un composant de lecture vidéo écrit sans React ; vous pouvez utiliser un Effet pour en appeler les méthodes afin que son état soit raccord avec l'état local de votre composant React. L'Effet ci-dessous crée une instance de la classe `MapWidget` définie dans `map-widget.js`. Lorsque vous modifiez la prop `zoomLevel` du composant `Map`, l'Effet appelle la méthode `setZoom()` sur l'instance pour la garder synchronisée ! @@ -818,9 +818,9 @@ export default function App() { const [zoomLevel, setZoomLevel] = useState(0); return ( <> - Zoom level: {zoomLevel}x + Niveau de zoom : {zoomLevel}x - +
    @@ -888,15 +888,15 @@ button { margin: 5px; }
    -In this example, a cleanup function is not needed because the `MapWidget` class manages only the DOM node that was passed to it. After the `Map` React component is removed from the tree, both the DOM node and the `MapWidget` class instance will be automatically garbage-collected by the browser JavaScript engine. +Dans cet exemple, nous n'avons pas besoin d'une fonction de nettoyage parce que la classe `MapWidget` ne gère que le nœud DOM qui lui a été passé. Après que le composant React `Map` aura été retiré de l'arborescence, tant le nœud DOM que l'instance de `MapWidget` seront automatiquement nettoyés par le *garbage collector* du moteur JavaScript du navigateur. --- -### Fetching data with Effects {/*fetching-data-with-effects*/} +### Charger des données avec les Effets {/*fetching-data-with-effects*/} -You can use an Effect to fetch data for your component. Note that [if you use a framework,](/learn/start-a-new-react-project#production-grade-react-frameworks) using your framework's data fetching mechanism will be a lot more efficient than writing Effects manually. +Vous pouvez utiliser un Effet pour charger des données pour votre composant. Remarquez que [si vous utilisez un framework](/learn/start-a-new-react-project#production-grade-react-frameworks), il sera nettement préférable d'utiliser les mécanismes de chargement de données de votre framework plutôt que le faire manuellement dans des Effets, notamment pour des questions de performances. -If you want to fetch data from an Effect manually, your code might look like this: +Si vous souhaitez charger des données manuellement depuis votre Effet, votre code ressemblera à ceci : ```js import { useState, useEffect } from 'react'; @@ -922,7 +922,7 @@ export default function Page() { // ... ``` -Note the `ignore` variable which is initialized to `false`, and is set to `true` during cleanup. This ensures [your code doesn't suffer from "race conditions":](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect) network responses may arrive in a different order than you sent them. +Remarquez la variable `ignore`, qui est initialisée à `false` mais mise à `true` lors du nettoyage. Ça garantit que [votre code ne souffrira pas de *“race conditions”*](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect) : les réponses réseau pourraient arriver dans un ordre différent de celui de vos envois de requêtes. @@ -953,10 +953,10 @@ export default function Page() { }}> - +
    -

    {bio ?? 'Loading...'}

    +

    {bio ?? 'Chargement...'}

    ); } @@ -967,7 +967,7 @@ export async function fetchBio(person) { const delay = person === 'Bob' ? 2000 : 200; return new Promise(resolve => { setTimeout(() => { - resolve('This is ' + person + '’s bio.'); + resolve('Voici la bio de ' + person + '.'); }, delay); }) } @@ -975,7 +975,7 @@ export async function fetchBio(person) {
    -You can also rewrite using the [`async` / `await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) syntax, but you still need to provide a cleanup function: +Vous pouvez aussi le réécrire en utilisant la syntaxe [`async` / `await`](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Statements/async_function), mais il vous faudra quand même une fonction de nettoyage : @@ -1009,10 +1009,10 @@ export default function Page() { }}> - +
    -

    {bio ?? 'Loading...'}

    +

    {bio ?? 'Chargement...'}

    ); } @@ -1023,7 +1023,7 @@ export async function fetchBio(person) { const delay = person === 'Bob' ? 2000 : 200; return new Promise(resolve => { setTimeout(() => { - resolve('This is ' + person + '’s bio.'); + resolve('Voici la bio de ' + person + '.'); }, delay); }) } @@ -1031,55 +1031,55 @@ export async function fetchBio(person) {
    -Writing data fetching directly in Effects gets repetitive and makes it difficult to add optimizations like caching and server rendering later. [It's easier to use a custom Hook--either your own or maintained by the community.](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks) +Implémenter le chargement de données directement dans les Effets devient vite répétitif et complexifie l'ajout ultérieur d'optimisations telles que la mise en cache ou le rendu côté serveur. [Il est plus facile d'utiliser un Hook personnalisé — qu'il soit de vous ou maintenu par la communauté](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks). -#### What are good alternatives to data fetching in Effects? {/*what-are-good-alternatives-to-data-fetching-in-effects*/} +#### Que préférer au chargement de données dans les Effets ? {/*what-are-good-alternatives-to-data-fetching-in-effects*/} -Writing `fetch` calls inside Effects is a [popular way to fetch data](https://www.robinwieruch.de/react-hooks-fetch-data/), especially in fully client-side apps. This is, however, a very manual approach and it has significant downsides: +Écrire nos appels `fetch` dans les Effets constitue [une façon populaire de charger des données](https://www.robinwieruch.de/react-hooks-fetch-data/), en particulier pour des applications entièrement côté client. Il s’agit toutefois d’une approche de bas niveau qui comporte plusieurs inconvénients significatifs : -- **Effects don't run on the server.** This means that the initial server-rendered HTML will only include a loading state with no data. The client computer will have to download all JavaScript and render your app only to discover that now it needs to load the data. This is not very efficient. -- **Fetching directly in Effects makes it easy to create "network waterfalls".** You render the parent component, it fetches some data, renders the child components, and then they start fetching their data. If the network is not very fast, this is significantly slower than fetching all data in parallel. -- **Fetching directly in Effects usually means you don't preload or cache data.** For example, if the component unmounts and then mounts again, it would have to fetch the data again. -- **It's not very ergonomic.** There's quite a bit of boilerplate code involved when writing `fetch` calls in a way that doesn't suffer from bugs like [race conditions.](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect) +- **Les Effets ne fonctionnent pas côté serveur.** Ça implique que le HTML rendu côté serveur avec React proposera un état initial sans données chargées. Le poste client devra télécharger tout le JavaScript et afficher l’appli pour découvrir seulement alors qu’il lui faut aussi charger des données. Ce n’est pas très efficace. +- **Charger depuis les Effets entraîne souvent des « cascades réseau ».** On affiche le composant parent, il charge ses données, affiche ses composants enfants, qui commencent seulement alors à charger leurs propres données. Si le réseau n’est pas ultra-rapide, cette séquence est nettement plus lente que le chargement parallèle de toutes les données concernées. +- **Charger depuis les Effets implique généralement l’absence de pré-chargement ou de cache des données.** Par exemple, si le composant est démonté puis remonté, il lui faudrait charger à nouveau les données dont il a besoin. +- **L’ergonomie n’est pas top.** Écrire ce genre d’appels `fetch` manuels nécessite pas mal de code générique, surtout lorsqu’on veut éviter des bugs tels que les [*race conditions*](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect). -This list of downsides is not specific to React. It applies to fetching data on mount with any library. Like with routing, data fetching is not trivial to do well, so we recommend the following approaches: +Cette liste d’inconvénients n’est d’ailleurs pas spécifique à React. Elle s’applique au chargement de données lors du montage quelle que soit la bibliothèque. Comme pour le routage, bien orchestrer son chargement de données est un exercice délicat, c’est pourquoi nous vous recommandons plutôt les approches suivantes : -- **If you use a [framework](/learn/start-a-new-react-project#production-grade-react-frameworks), use its built-in data fetching mechanism.** Modern React frameworks have integrated data fetching mechanisms that are efficient and don't suffer from the above pitfalls. -- **Otherwise, consider using or building a client-side cache.** Popular open source solutions include [React Query](https://react-query.tanstack.com/), [useSWR](https://swr.vercel.app/), and [React Router 6.4+.](https://beta.reactrouter.com/en/main/start/overview) You can build your own solution too, in which case you would use Effects under the hood but also add logic for deduplicating requests, caching responses, and avoiding network waterfalls (by preloading data or hoisting data requirements to routes). +- **Si vous utilisez un [framework](/learn/start-a-new-react-project#production-grade-react-frameworks), utilisez son mécanisme intégré de chargement de données.** Les frameworks React modernes ont intégré le chargement de données de façon efficace afin d’éviter ce type d’ornières. +- **Dans le cas contraire, envisagez l’utilisation ou la construction d’un cache côté client.** Les solutions open-source les plus populaires incluent [React Query](https://tanstack.com/query/latest), [useSWR](https://swr.vercel.app/), et [React Router 6.4+](https://beta.reactrouter.com/en/main/start/overview). Vous pouvez aussi construire votre propre solution, auquel cas vous utiliseriez sans doute les Effets sous le capot, mais ajouteriez la logique nécessaire au dédoublonnement de requêtes, à la mise en cache des réponses, et à l’optimisation des cascades réseau (en préchargeant les données ou en consolidant vers le haut les besoins de données des routes). -You can continue fetching data directly in Effects if neither of these approaches suit you. +Vous pouvez continuer à charger les données directement dans les Effets si aucune de ces approches ne vous convient. --- -### Specifying reactive dependencies {/*specifying-reactive-dependencies*/} +### Spécifier les dépendances réactives {/*specifying-reactive-dependencies*/} -**Notice that you can't "choose" the dependencies of your Effect.** Every reactive value used by your Effect's code must be declared as a dependency. Your Effect's dependency list is determined by the surrounding code: +**Remarquez bien que vous ne pouvez pas « choisir » les dépendances de votre Effet.** Chaque valeur réactive utilisée par le code de votre Effet doit être déclarée dans votre liste de dépendances, laquelle découle donc du code environnant : ```js [[2, 1, "roomId"], [2, 2, "serverUrl"], [2, 5, "serverUrl"], [2, 5, "roomId"], [2, 8, "serverUrl"], [2, 8, "roomId"]] -function ChatRoom({ roomId }) { // This is a reactive value - const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // This is a reactive value too +function ChatRoom({ roomId }) { // C’est une valeur réactive + const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // Ça aussi useEffect(() => { - const connection = createConnection(serverUrl, roomId); // This Effect reads these reactive values + const connection = createConnection(serverUrl, roomId); // Cet Effet lit ces valeurs réactives connection.connect(); return () => connection.disconnect(); - }, [serverUrl, roomId]); // ✅ So you must specify them as dependencies of your Effect + }, [serverUrl, roomId]); // ✅ Vous devez donc les lister comme dépendances de votre Effet // ... } ``` -If either `serverUrl` or `roomId` change, your Effect will reconnect to the chat using the new values. +Si `serverUrl` ou `roomId` change, votre Effet se reconnectera à la discussion en utilisant leurs valeurs à jour. -**[Reactive values](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) include props and all variables and functions declared directly inside of your component.** Since `roomId` and `serverUrl` are reactive values, you can't remove them from the dependencies. If you try to omit them and [your linter is correctly configured for React,](/learn/editor-setup#linting) the linter will flag this as a mistake you need to fix: +**[Les valeurs réactives](/learn/lifecycle-of-reactive-effects#all-variables-declared-in-the-component-body-are-reactive) comprennent les props et toutes les variables et fonctions déclarées directement au sein de votre composant.** Dans la mesure où `roomId` et `serverUrl`sont des valeurs réactives, vous ne pouvez pas les retirer de la liste des dépendances. Si vous tentiez de les retirer et que [votre *linter* est correctement configuré pour React](/learn/editor-setup#linting), il vous l'interdirait : ```js {8} function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); - + useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); @@ -1089,68 +1089,68 @@ function ChatRoom({ roomId }) { } ``` -**To remove a dependency, you need to ["prove" to the linter that it *doesn't need* to be a dependency.](/learn/removing-effect-dependencies#removing-unnecessary-dependencies)** For example, you can move `serverUrl` out of your component to prove that it's not reactive and won't change on re-renders: +**Pour retirer une dépendance, [« prouvez » au *linter* qu'elle n'a *pas besoin* d'être une dépendance](/learn/removing-effect-dependencies#removing-unnecessary-dependencies).** Par exemple, vous pouvez déplacer `serverUrl` hors de votre composant pour lui prouver qu'elle n'est pas réactive et ne changera pas d'un rendu à l'autre : ```js {1,8} -const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore +const serverUrl = 'https://localhost:1234'; // Ce n’est plus une valeur réactive function ChatRoom({ roomId }) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Toutes les dépendances sont déclarées // ... } ``` -Now that `serverUrl` is not a reactive value (and can't change on a re-render), it doesn't need to be a dependency. **If your Effect's code doesn't use any reactive values, its dependency list should be empty (`[]`):** +À présent que `serverUrl` n'est plus une valeur réactive (et ne peut plus changer d'un rendu à l'autre), elle n'a plus besoin d'être déclarée comme dépendance. **Si le code de votre Effet n'utilise aucune valeur réactive, sa liste de dépendances devrait être vide (`[]`) :** ```js {1,2,9} -const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore -const roomId = 'music'; // Not a reactive value anymore +const serverUrl = 'https://localhost:1234'; // Ce n’est plus une valeur réactive +const roomId = 'music'; // Ce n’est plus une valeur réactive function ChatRoom() { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, []); // ✅ All dependencies declared + }, []); // ✅ Toutes les dépendances sont déclarées // ... } ``` -[An Effect with empty dependencies](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) doesn't re-run when any of your component's props or state change. +[Un Effet avec des dépendances vides](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) n'est pas re-exécuté lorsque les valeurs des props ou de l'état de votre composant changent. -If you have an existing codebase, you might have some Effects that suppress the linter like this: +Si vous avez une base de code existante, vous trouverez peut-être des Effets qui réduisent le *linter* au silence comme ceci : ```js {3-4} useEffect(() => { // ... - // 🔴 Avoid suppressing the linter like this: + // 🔴 Évitez de réduire ainsi le *linter* au silence : // eslint-ignore-next-line react-hooks/exhaustive-deps }, []); ``` -**When dependencies don't match the code, there is a high risk of introducing bugs.** By suppressing the linter, you "lie" to React about the values your Effect depends on. [Instead, prove they're unnecessary.](/learn/removing-effect-dependencies#removing-unnecessary-dependencies) +**Lorsque les dépendances ne correspondent pas au code, il y a un risque élevé de bugs.** En réduisant le *linter* au silence, vous « mentez » à React quant aux valeurs dont dépend votre Effet. [Au lieu de ça, prouvez qu"elles sont superflues](/learn/removing-effect-dependencies#removing-unnecessary-dependencies). - + -#### Passing a dependency array {/*passing-a-dependency-array*/} +#### Passer un tableau de dépendances {/*passing-a-dependency-array*/} -If you specify the dependencies, your Effect runs **after the initial render _and_ after re-renders with changed dependencies.** +Si vous spécifiez des dépendances, votre Effet est exécuté **après le rendu initial *et* après les nouveaux rendus qui modifient ces dépendances**. ```js {3} useEffect(() => { // ... -}, [a, b]); // Runs again if a or b are different +}, [a, b]); // Re-exécuté si a ou b ont changé ``` -In the below example, `serverUrl` and `roomId` are [reactive values,](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) so they both must be specified as dependencies. As a result, selecting a different room in the dropdown or editing the server URL input causes the chat to re-connect. However, since `message` isn't used in the Effect (and so it isn't a dependency), editing the message doesn't re-connect to the chat. +Dans l'exemple ci-dessous, `serverUrl` et `roomId` sont des [valeurs réactives](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values), qui doivent donc toutes les deux être listées comme dépendances. Du coup, sélectionner un autre salon dans la liste déroulante ou modifier l'URL du serveur dans le champ de saisie entraînera une reconnexion de la discussion. En revanche, puisque `message` n'est pas utilisé par l'Effet (et n'est donc pas une dépendance), modifier le message n'entraîne pas de reconnexion. @@ -1173,15 +1173,15 @@ function ChatRoom({ roomId }) { return ( <> -

    Welcome to the {roomId} room!

    +

    Bienvenue dans le salon {roomId} !

    @@ -1194,17 +1194,17 @@ export default function App() { return ( <> {show &&
    } @@ -1216,13 +1216,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Une véritable implémentation se connecterait en vrai au serveur return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Connexion au salon « ' + roomId + ' » sur ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Déconnexion du salon « ' + roomId + ' » sur ' + serverUrl); } }; } @@ -1237,20 +1237,19 @@ button { margin-left: 5px; } -#### Passing an empty dependency array {/*passing-an-empty-dependency-array*/} +#### Passer un tableau de dépendances vide {/*passing-an-empty-dependency-array*/} -If your Effect truly doesn't use any reactive values, it will only run **after the initial render.** +Si votre Effet n'utilise effectivement aucune valeur réactive, il ne s'exécutera **qu'après le rendu initial**. ```js {3} useEffect(() => { // ... -}, []); // Does not run again (except once in development) +}, []); // Exécuté une seule fois (deux en développement) ``` -**Even with empty dependencies, setup and cleanup will [run one extra time in development](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) to help you find bugs.** - +**Même avec des dépendances vides, la mise en place et le nettoyage seront [exécutés une fois de plus en développement](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) pour vous aider à repérer d'éventuels bugs.** -In this example, both `serverUrl` and `roomId` are hardcoded. Since they're declared outside the component, they are not reactive values, and so they aren't dependencies. The dependency list is empty, so the Effect doesn't re-run on re-renders. +Dans cet exemple, `serverUrl` et `roomId` sont tous les deux codés en dur. Puisqu'ils sont déclarés hors du composant, ils ne constituent pas des valeurs réactives, et ne sont donc pas des dépendances. La liste de dépendances est vide, de sorte que l'Effet n'est pas re-exécuté lors des rendus ultérieurs. @@ -1272,9 +1271,9 @@ function ChatRoom() { return ( <> -

    Welcome to the {roomId} room!

    +

    Bienvenue dans le salon {roomId} !

    @@ -1286,7 +1285,7 @@ export default function App() { return ( <> {show &&
    } {show && } @@ -1297,13 +1296,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Une véritable implémentation se connecterait en vrai au serveur return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Connexion au salon « ' + roomId + ' » sur ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Déconnexion du salon « ' + roomId + ' » sur ' + serverUrl); } }; } @@ -1314,17 +1313,17 @@ export function createConnection(serverUrl, roomId) { -#### Passing no dependency array at all {/*passing-no-dependency-array-at-all*/} +#### Ne pas passer de dépendances du tout {/*passing-no-dependency-array-at-all*/} -If you pass no dependency array at all, your Effect runs **after every single render (and re-render)** of your component. +Si vous ne passez aucun tableau de dépendances, votre Effet sera exécuté **après chaque rendu (rendu initial comme rendus ultérieurs)** de votre composant. ```js {3} useEffect(() => { // ... -}); // Always runs again +}); // Exécuté à chaque fois ``` -In this example, the Effect re-runs when you change `serverUrl` and `roomId`, which is sensible. However, it *also* re-runs when you change the `message`, which is probably undesirable. This is why usually you'll specify the dependency array. +Dans cet exemple, l'Effet est re-exécuté quand vous changez `serverUrl` ou `roomId`, ce qui est logique. Cependant, il est *aussi* re-exécuté lorsque vous modifiez `message`, ce qui n'est sans doute pas souhaitable. C'est pourquoi il vous faut spécifier un tableau de dépendances. @@ -1342,20 +1341,20 @@ function ChatRoom({ roomId }) { return () => { connection.disconnect(); }; - }); // No dependency array at all + }); // Aucun tableau de dépendances return ( <> -

    Welcome to the {roomId} room!

    +

    Bienvenue dans le salon {roomId} !

    @@ -1368,17 +1367,17 @@ export default function App() { return ( <> {show &&
    } @@ -1390,13 +1389,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Une véritable implémentation se connecterait en vrai au serveur return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Connexion au salon « ' + roomId + ' » sur ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Déconnexion du salon « ' + roomId + ' » sur ' + serverUrl); } }; } @@ -1415,9 +1414,9 @@ button { margin-left: 5px; } --- -### Updating state based on previous state from an Effect {/*updating-state-based-on-previous-state-from-an-effect*/} +### Mettre à jour l'état sur base d'un état précédent, au sein d'un Effet {/*updating-state-based-on-previous-state-from-an-effect*/} -When you want to update state based on previous state from an Effect, you might run into a problem: +Lorsque vous souhaitez mettre à jour l'état sur base d'un état précédent depuis un Effet, vous risquez de rencontrer un problème : ```js {6,9} function Counter() { @@ -1425,17 +1424,17 @@ function Counter() { useEffect(() => { const intervalId = setInterval(() => { - setCount(count + 1); // You want to increment the counter every second... + setCount(count + 1); // Vous souhaitez incrémenter le compteur à chaque seconde... }, 1000) return () => clearInterval(intervalId); - }, [count]); // 🚩 ... but specifying `count` as a dependency always resets the interval. + }, [count]); // 🚩 ... mais préciser `count` comme dépendance réinitialise l'intervalle à chaque fois. // ... } ``` -Since `count` is a reactive value, it must be specified in the list of dependencies. However, that causes the Effect to cleanup and setup again every time the `count` changes. This is not ideal. +Dans la mesure où `count` est une valeur réactive, elle doit figurer dans la liste des dépendances. Pourtant, ça force l'Effet à se nettoyer et se remettre en place chaque fois que `count` change. C'est loin d'être idéal. -To fix this, [pass the `c => c + 1` state updater](/reference/react/useState#updating-state-based-on-the-previous-state) to `setCount`: +Pour corriger ça, [passez une fonction de mise à jour d'état `c => c + 1`](/reference/react/useState#updating-state-based-on-the-previous-state) à `setCount` : @@ -1447,10 +1446,10 @@ export default function Counter() { useEffect(() => { const intervalId = setInterval(() => { - setCount(c => c + 1); // ✅ Pass a state updater + setCount(c => c + 1); // ✅ Passe une fonction de mise à jour }, 1000); return () => clearInterval(intervalId); - }, []); // ✅ Now count is not a dependency + }, []); // ✅ count n’est plus une dépendance return

    {count}

    ; } @@ -1470,14 +1469,14 @@ body {
    -Now that you're passing `c => c + 1` instead of `count + 1`, [your Effect no longer needs to depend on `count`.](/learn/removing-effect-dependencies#are-you-reading-some-state-to-calculate-the-next-state) As a result of this fix, it won't need to cleanup and setup the interval again every time the `count` changes. +Maintenant que vous passez `c => c + 1` au lieu de `count + 1`, [votre Effet n'a plus besoin de dépendre de `count`](/learn/removing-effect-dependencies#are-you-reading-some-state-to-calculate-the-next-state). En conséquence, il n'aura plus besoin de nettoyer et remettre en place l'intervalle chaque fois que `count` change. --- -### Removing unnecessary object dependencies {/*removing-unnecessary-object-dependencies*/} +### Supprimer des dépendances objets superflues {/*removing-unnecessary-object-dependencies*/} -If your Effect depends on an object or a function created during rendering, it might run too often. For example, this Effect re-connects after every render because the `options` object is [different for every render:](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) +Si votre Effet dépend d'un objet ou d'une fonction créée lors du rendu, il s'exécutera sans doute trop souvent. Par exemple, cet Effet se reconnecte à chaque rendu parce que l'objet `options` [est en réalité un objet différent à chaque rendu](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) : ```js {6-9,12,15} const serverUrl = 'https://localhost:1234'; @@ -1485,20 +1484,20 @@ const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); - const options = { // 🚩 This object is created from scratch on every re-render + const options = { // 🚩 Cet objet est (re)créé à chaque rendu serverUrl: serverUrl, roomId: roomId }; useEffect(() => { - const connection = createConnection(options); // It's used inside the Effect + const connection = createConnection(options); // L’Effet l’utilise connection.connect(); return () => connection.disconnect(); - }, [options]); // 🚩 As a result, these dependencies are always different on a re-render + }, [options]); // 🚩 Les dépendances sont donc différentes à chaque rendu // ... ``` -Avoid using an object created during rendering as a dependency. Instead, create the object inside the Effect: +Évitez d'utiliser un objet créé lors du rendu comme dépendance. Préférez créer cet objet au sein de l'Effet : @@ -1523,7 +1522,7 @@ function ChatRoom({ roomId }) { return ( <> -

    Welcome to the {roomId} room!

    +

    Bienvenue dans le salon {roomId} !

    setMessage(e.target.value)} /> ); @@ -1534,14 +1533,14 @@ export default function App() { return ( <>
    @@ -1553,13 +1552,13 @@ export default function App() { ```js chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Une véritable implémentation se connecterait en vrai au serveur return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Connexion au salon « ' + roomId + ' » sur ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Déconnexion du salon « ' + roomId + ' » sur ' + serverUrl); } }; } @@ -1572,21 +1571,22 @@ button { margin-left: 10px; }
    -Now that you create the `options` object inside the Effect, the Effect itself only depends on the `roomId` string. +Maintenant que vous créez l'objet `options` au sein de l'Effet, l'Effet lui-même ne dépend plus que de la chaîne de caractères `roomId`. -With this fix, typing into the input doesn't reconnect the chat. Unlike an object which gets re-created, a string like `roomId` doesn't change unless you set it to another value. [Read more about removing dependencies.](/learn/removing-effect-dependencies) +Grâce à ce correctif, modifier la saisie ne reconnecte pas la discussion. Contrairement à un objet créé de frais à chaque fois, un texte comme `roomId` ne change pas tant qu'on n'en modifie pas la valeur. [Apprenez-en davantage sur l'allègement des dépendances](/learn/removing-effect-dependencies). --- -### Removing unnecessary function dependencies {/*removing-unnecessary-function-dependencies*/} +### Supprimer des dépendances fonctions superflues {/*removing-unnecessary-function-dependencies*/} + +Si votre Effet dépend d'un objet ou d'une fonction créée lors du rendu, il s'exécutera sans doute trop souvent. Par exemple, cet Effet se reconnecte à chaque rendu parce que la fonction `createOptions` [est une fonction différente à chaque rendu](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) : -If your Effect depends on an object or a function created during rendering, it might run too often. For example, this Effect re-connects after every render because the `createOptions` function is [different for every render:](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) ```js {4-9,12,16} function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); - function createOptions() { // 🚩 This function is created from scratch on every re-render + function createOptions() { // 🚩 Cette fonction est (re)créée à chaque rendu return { serverUrl: serverUrl, roomId: roomId @@ -1594,17 +1594,17 @@ function ChatRoom({ roomId }) { } useEffect(() => { - const options = createOptions(); // It's used inside the Effect + const options = createOptions(); // L’Effet l’utilise const connection = createConnection(); connection.connect(); return () => connection.disconnect(); - }, [createOptions]); // 🚩 As a result, these dependencies are always different on a re-render + }, [createOptions]); // 🚩 Les dépendances sont donc différentes à chaque rendu // ... ``` -By itself, creating a function from scratch on every re-render is not a problem. You don't need to optimize that. However, if you use it as a dependency of your Effect, it will cause your Effect to re-run after every re-render. +En soi, créer une fonction à chaque rendu n'est pas un problème. Vous n'avez pas besoin d'optimiser ça. Mais si vous l'utilisez comme dépendance d'un Effet, elle forcera votre Effet à être ré-exécuté à chaque rendu. -Avoid using a function created during rendering as a dependency. Instead, declare it inside the Effect: +Évitez d'utiliser une fonction créée lors du rendu comme dépendance. Déclarez-la plutôt au sein de l'Effet : @@ -1633,7 +1633,7 @@ function ChatRoom({ roomId }) { return ( <> -

    Welcome to the {roomId} room!

    +

    Bienvenue dans le salon {roomId} !

    setMessage(e.target.value)} /> ); @@ -1644,14 +1644,14 @@ export default function App() { return ( <>
    @@ -1663,13 +1663,13 @@ export default function App() { ```js chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Une véritable implémentation se connecterait en vrai au serveur return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Connexion au salon « ' + roomId + ' » sur ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Déconnexion du salon « ' + roomId + ' » sur ' + serverUrl); } }; } @@ -1682,32 +1682,34 @@ button { margin-left: 10px; }
    -Now that you define the `createOptions` function inside the Effect, the Effect itself only depends on the `roomId` string. With this fix, typing into the input doesn't reconnect the chat. Unlike a function which gets re-created, a string like `roomId` doesn't change unless you set it to another value. [Read more about removing dependencies.](/learn/removing-effect-dependencies) +Maintenant que vous déclarez la fonction `createOptions` au sein de l'Effet, l'Effet lui-même ne dépend plus que de la chaîne de caractères `roomId`. + +Grâce à ce correctif, modifier la saisie ne reconnecte pas la discussion. Contrairement à une fonction créée de frais à chaque fois, un texte comme `roomId` ne change pas tant qu'on n'en modifie pas la valeur. [Apprenez-en davantage sur l'allègement des dépendances](/learn/removing-effect-dependencies). --- -### Reading the latest props and state from an Effect {/*reading-the-latest-props-and-state-from-an-effect*/} +### Lire les dernières props et états à jour depuis un Effet {/*reading-the-latest-props-and-state-from-an-effect*/} -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 : elle n’a donc pas encore été livrée** dans une version stable de React. -By default, when you read a reactive value from an Effect, you have to add it as a dependency. This ensures that your Effect "reacts" to every change of that value. For most dependencies, that's the behavior you want. +Par défaut, lorsque vous lisez une valeur réactive depuis un Effet, vous devez l'ajouter comme dépendance. Ça garantit que votre Effet « réagit » à chaque modification de cette valeur. Pour la plupart des dépendances, c'est bien le comportement que vous souhaitez. -**However, sometimes you'll want to read the *latest* props and state from an Effect without "reacting" to them.** For example, imagine you want to log the number of the items in the shopping cart for every page visit: +**Toutefois, il peut arriver que vous souhaitiez lire les *dernières* valeurs à jour de props ou d'états depuis un Effet, sans pour autant y « réagir ».** Imaginons par exemple que vous souhaitiez afficher en console le nombre d'éléments dans le panier d'achats à chaque visite de la page : ```js {3} function Page({ url, shoppingCart }) { useEffect(() => { logVisit(url, shoppingCart.length); - }, [url, shoppingCart]); // ✅ All dependencies declared + }, [url, shoppingCart]); // ✅ Toutes les dépendances sont déclarées // ... } ``` -**What if you want to log a new page visit after every `url` change, but *not* if only the `shoppingCart` changes?** You can't exclude `shoppingCart` from dependencies without breaking the [reactivity rules.](#specifying-reactive-dependencies) However, you can express that you *don't want* a piece of code to "react" to changes even though it is called from inside an Effect. [Declare an *Effect Event*](/learn/separating-events-from-effects#declaring-an-effect-event) with the [`useEffectEvent`](/reference/react/experimental_useEffectEvent) Hook, and move the code reading `shoppingCart` inside of it: +**Et si vous vouliez afficher une visite de page après chaque modification de `url`, mais *pas* lorsque seul `shoppingCart` change ?** Vous ne pouvez pas exclure `shoppingCart` de vos dépendances sans enfreindre les [règles de la réactivité](#specifying-reactive-dependencies). En revanche, vous pouvez exprimer que vous *ne souhaitez pas* qu'un bout de votre code « réagisse » aux changements, même s'il est appelé depuis un Effet. [Déclarez un *Événement d'Effet*](/learn/separating-events-from-effects#declaring-an-effect-event) avec le Hook [`useEffectEvent`](/reference/react/experimental_useEffectEvent), et déplacez le code qui consulte `shoppingCart` à l'intérieur : ```js {2-4,7,8} function Page({ url, shoppingCart }) { @@ -1717,23 +1719,23 @@ function Page({ url, shoppingCart }) { useEffect(() => { onVisit(url); - }, [url]); // ✅ All dependencies declared + }, [url]); // ✅ Toutes les dépendances sont déclarées // ... } ``` -**Effect Events are not reactive and must always be omitted from dependencies of your Effect.** This is what lets you put non-reactive code (where you can read the latest value of some props and state) inside of them. By reading `shoppingCart` inside of `onVisit`, you ensure that `shoppingCart` won't re-run your Effect. +**Les Événements d'Effets ne sont pas réactifs et doivent toujours être omis des dépendances de votre Effet.** C'est ce qui vous permet d'y mettre du code non réactif (qui peut donc lire la dernière valeur en date de props ou d'états). En lisant `shoppingCart` au sein de `onVisit`, vous garantissez que `shoppingCart` ne redéclenchera pas votre Effet. -[Read more about how Effect Events let you separate reactive and non-reactive code.](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events) +[Découvrez en quoi les Événements d'Effets vous permettent de séparer les codes réactif et non réactif](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events). --- -### Displaying different content on the server and the client {/*displaying-different-content-on-the-server-and-the-client*/} +### Afficher un contenu différent côté serveur et côté client {/*displaying-different-content-on-the-server-and-the-client*/} -If your app uses server rendering (either [directly](/reference/react-dom/server) or via a [framework](/learn/start-a-new-react-project#production-grade-react-frameworks)), your component will render in two different environments. On the server, it will render to produce the initial HTML. On the client, React will run the rendering code again so that it can attach your event handlers to that HTML. This is why, for [hydration](/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html) to work, your initial render output must be identical on the client and the server. +Si votre appli utilise du rendu côté serveur (que ce soit [en direct](/reference/react-dom/server) ou *via* un [framework](/learn/start-a-new-react-project#production-grade-react-frameworks)), votre composant fera son rendu dans deux environnements différents. Côté serveur, son rendu produira le HTML initial. Côté client, React exécutera à nouveau le code de rendu pour pouvoir inscrire les gestionnaires d'événements à ce HTML. C'est pourquoi, afin que [l'hydratation](/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html) puisse fonctionner, votre résultat de rendu initial doit être identique côté client et côté serveur. -In rare cases, you might need to display different content on the client. For example, if your app reads some data from [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), it can't possibly do that on the server. Here is how you could implement this: +Dans de rares cas, vous pourriez avoir besoin de produire des contenus distincts côté client. Disons par exemple que votre appli lit certaines données depuis [`localStorage`](https://developer.mozilla.org/fr/docs/Web/API/Window/localStorage), il ne peut clairement pas faire ça côté serveur. Voici comment vous implémenteriez ça : ```js function MyComponent() { @@ -1744,44 +1746,44 @@ function MyComponent() { }, []); if (didMount) { - // ... return client-only JSX ... + // ... renvoi du JSX pour le client seulement ... } else { - // ... return initial JSX ... + // ... renvoi du JSX initial ... } } ``` -While the app is loading, the user will see the initial render output. Then, when it's loaded and hydrated, your Effect will run and set `didMount` to `true`, triggering a re-render. This will switch to the client-only render output. Effects don't run on the server, so this is why `didMount` was `false` during the initial server render. +Pendant que l'appli charge, l'utilisateur voit le résultat du rendu initial. Puis, lorsqu'elle sera chargée et hydratée, votre Effet sera exécuté et définira `didMount` à `true`, ce qui déclenchera un nouveau rendu. On basculera alors sur le résultat de rendu pour le client seulement. Les Effets ne sont pas exécutés côté serveur, c'est pourquoi `didMount` resterait à `false` lors du rendu initial. -Use this pattern sparingly. Keep in mind that users with a slow connection will see the initial content for quite a bit of time--potentially, many seconds--so you don't want to make jarring changes to your component's appearance. In many cases, you can avoid the need for this by conditionally showing different things with CSS. +N'abusez pas de cette astuce. Gardez à l'esprit que les utilisateurs avec des connexions lentes verront le contenu initial pendant un bon bout de temps — jusqu'à plusieurs secondes — et qu'il faudrait donc éviter d'appliquer au final des changements trop drastiques dans l'apparence de votre composant. Le plus souvent, vous pourrez éviter de recourir à cette approche en utilisant des affichages conditionnels *via* CSS. --- -## Troubleshooting {/*troubleshooting*/} +## Dépannage {/*troubleshooting*/} -### My Effect runs twice when the component mounts {/*my-effect-runs-twice-when-the-component-mounts*/} +### Mon Effet est exécuté deux fois au montage du composant {/*my-effect-runs-twice-when-the-component-mounts*/} -When Strict Mode is on, in development, React runs setup and cleanup one extra time before the actual setup. +Lorsque le mode strict est activé, en développement, React exécutera une première fois la mise en place et le nettoyage, avant la mise en place effective. -This is a stress-test that verifies your Effect’s logic is implemented correctly. If this causes visible issues, your cleanup function is missing some logic. The cleanup function should stop or undo whatever the setup function was doing. The rule of thumb is that the user shouldn’t be able to distinguish between the setup being called once (as in production) and a setup → cleanup → setup sequence (as in development). +C'est une mise à l'épreuve pour vérifier que la logique de votre Effet est implémentée correctement. Si ça entraîne des problèmes, c'est que votre code de nettoyage est manquant ou incomplet. La fonction de nettoyage devrait arrêter ou défaire ce que la fonction de mise en place a initié. La règle à suivre est simple : l'utilisateur ne devrait pas pouvoir faire la différence entre une exécution unique de la mise en place (comme en production) et une séquence *mise en place* → *nettoyage* → *mise en place* (comme en développement). -Read more about [how this helps find bugs](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) and [how to fix your logic.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) +Découvrez [en quoi ça vous aide à repérer des bugs](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) et [comment corriger votre code](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development). --- -### My Effect runs after every re-render {/*my-effect-runs-after-every-re-render*/} +### Mon Effet est exécuté après chaque rendu {/*my-effect-runs-after-every-re-render*/} -First, check that you haven't forgotten to specify the dependency array: +Commencez par vérifier que vous n'avez pas oublié de spécifier le tableau des dépendances : ```js {3} useEffect(() => { // ... -}); // 🚩 No dependency array: re-runs after every render! +}); // 🚩 Aucun tableau de dépendance : exécuté après chaque rendu ! ``` -If you've specified the dependency array but your Effect still re-runs in a loop, it's because one of your dependencies is different on every re-render. +Si vous avez spécifié un tableau de dépendances et que votre Effet persiste à s'exécuter en boucle, c'est parce qu'une de vos dépendances est différente à chaque rendu. -You can debug this problem by manually logging your dependencies to the console: +Vous pouvez déboguer ce problème en affichant vos dépendances en console : ```js {5} useEffect(() => { @@ -1791,58 +1793,58 @@ You can debug this problem by manually logging your dependencies to the console: console.log([serverUrl, roomId]); ``` -You can then right-click on the arrays from different re-renders in the console and select "Store as a global variable" for both of them. Assuming the first one got saved as `temp1` and the second one got saved as `temp2`, you can then use the browser console to check whether each dependency in both arrays is the same: +Vous pouvez alors cliquer bouton droit, dans la console, sur les tableaux issus de différents rendus et sélectionner « Stocker objet en tant que variable globale » pour chacun d'entre eux. En supposant que vous avez stocké le premier en tant que `temp1` et le second en tant que `temp2`, vous pouvez alors utiliser la console du navigateur pour vérifier si chaque dépendance des tableaux est identique : ```js -Object.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays? -Object.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays? -Object.is(temp1[2], temp2[2]); // ... and so on for every dependency ... +Object.is(temp1[0], temp2[0]); // La première dépendance est-elle inchangée ? +Object.is(temp1[1], temp2[1]); // La deuxième dépendance est-elle inchangée ? +Object.is(temp1[2], temp2[2]); // ... et ainsi de suite pour chaque dépendance ... ``` -When you find the dependency that is different on every re-render, you can usually fix it in one of these ways: +Lorsque vous aurez repéré la dépendance qui diffère d'un rendu à l'autre, vous pouvez généralement corriger ça de l'une des manières suivantes : -- [Updating state based on previous state from an Effect](#updating-state-based-on-previous-state-from-an-effect) -- [Removing unnecessary object dependencies](#removing-unnecessary-object-dependencies) -- [Removing unnecessary function dependencies](#removing-unnecessary-function-dependencies) -- [Reading the latest props and state from an Effect](#reading-the-latest-props-and-state-from-an-effect) +- [Mettre à jour l'état sur base d'un état précédent, au sein d'un Effet](#updating-state-based-on-previous-state-from-an-effect) +- [Supprimer des dépendances objets superflues](#removing-unnecessary-object-dependencies) +- [Supprimer des dépendances fonctions superflues](#removing-unnecessary-function-dependencies) +- [Lire les dernières props et états à jour depuis un Effet](#reading-the-latest-props-and-state-from-an-effect) -As a last resort (if these methods didn't help), wrap its creation with [`useMemo`](/reference/react/useMemo#memoizing-a-dependency-of-another-hook) or [`useCallback`](/reference/react/useCallback#preventing-an-effect-from-firing-too-often) (for functions). +En tout dernier recours (si aucune de ces approches n'a résolu le souci), enrobez la création de la dépendance avec [`useMemo`](/reference/react/useMemo#memoizing-a-dependency-of-another-hook) (ou [`useCallback`](/reference/react/useCallback#preventing-an-effect-from-firing-too-often) pour les fonctions). --- -### My Effect keeps re-running in an infinite cycle {/*my-effect-keeps-re-running-in-an-infinite-cycle*/} +### Mon Effet n'arrête pas de se re-exécuter {/*my-effect-keeps-re-running-in-an-infinite-cycle*/} -If your Effect runs in an infinite cycle, these two things must be true: +Si votre Effet s'exécute en boucle infinie, deux choses devraient se passer : -- Your Effect is updating some state. -- That state leads to a re-render, which causes the Effect's dependencies to change. +- Votre Effet met à jour un état. +- Cet état entraîne un nouveau rendu, qui modifie les dépendances de votre Effet. -Before you start fixing the problem, ask yourself whether your Effect is connecting to some external system (like DOM, network, a third-party widget, and so on). Why does your Effect need to set state? Does it synchronize with that external system? Or are you trying to manage your application's data flow with it? +Avant de vous attaquer à la résolution de ce problème, demandez-vous si votre Effet se connecte à un système extérieur (tel que le DOM, le réseau, un widget tiers, etc.). Pourquoi votre Effet a-t-il besoin de modifier l'état ? Se synchronise-t-il avec un système extérieur ? Ou essayez-vous de gérer le flux de données de votre application avec ça ? -If there is no external system, consider whether [removing the Effect altogether](/learn/you-might-not-need-an-effect) would simplify your logic. +S'il n'y a pas de système extérieur, envisagez de [retirer carrément l'Effet](/learn/you-might-not-need-an-effect) pour simplifier votre logique. -If you're genuinely synchronizing with some external system, think about why and under what conditions your Effect should update the state. Has something changed that affects your component's visual output? If you need to keep track of some data that isn't used by rendering, a [ref](/reference/react/useRef#referencing-a-value-with-a-ref) (which doesn't trigger re-renders) might be more appropriate. Verify your Effect doesn't update the state (and trigger re-renders) more than needed. +Si vous vous synchronisez effectivement avec un système extérieur, réfléchissez aux conditions dans lesquelles votre Effet devrait mettre à jour l'état. Quelque chose a-t-il changé qui impacte le résultat visuel de votre composant ? Si vous devez surveiller certaines données inutilisées par le rendu, une [ref](/reference/react/useRef#referencing-a-value-with-a-ref) (qui ne redéclenche pas de rendu) serait peut-être plus appropriée. Vérifiez que votre Effet ne met pas à jour l'état (entraînant un nouveau rendu) plus que nécessaire. -Finally, if your Effect is updating the state at the right time, but there is still a loop, it's because that state update leads to one of the Effect's dependencies changing. [Read how to debug dependency changes.](/reference/react/useEffect#my-effect-runs-after-every-re-render) +Pour finir, si votre Effet met à jour l'état au bon moment, mais qu'il y a tout de même une boucle, c'est sans doute parce que la mise à jour de l'état entraîne la modification d'une des dépendances de l'Effet. [Voyez comment déboguer les modifications de dépendances](#my-effect-runs-after-every-re-render). --- -### My cleanup logic runs even though my component didn't unmount {/*my-cleanup-logic-runs-even-though-my-component-didnt-unmount*/} +### Ma logique de nettoyage est exécutée alors que mon composant n'a pas été démonté {/*my-cleanup-logic-runs-even-though-my-component-didnt-unmount*/} -The cleanup function runs not only during unmount, but before every re-render with changed dependencies. Additionally, in development, React [runs setup+cleanup one extra time immediately after component mounts.](#my-effect-runs-twice-when-the-component-mounts) +La fonction de nettoyage n'est pas exécutée seulement lors du démontage, mais avant chaque nouveau rendu dont les dépendances ont changé. Qui plus est, en développement, React [exécute la mise en place et le nettoyage une fois de plus juste après le montage du composant](#my-effect-runs-twice-when-the-component-mounts). -If you have cleanup code without corresponding setup code, it's usually a code smell: +Si vous avez du code de nettoyage qui ne correspond à aucun code de mise en place, c'est généralement mauvaise signe : ```js {2-5} useEffect(() => { - // 🔴 Avoid: Cleanup logic without corresponding setup logic + // 🔴 À éviter : code de nettoyage sans mise en place correspondante return () => { doSomething(); }; }, []); ``` -Your cleanup logic should be "symmetrical" to the setup logic, and should stop or undo whatever setup did: +Votre code de nettoyage devrait refléter celui de mise en place, qu'il devrait arrêter ou défaire : ```js {2-3,5} useEffect(() => { @@ -1854,10 +1856,10 @@ Your cleanup logic should be "symmetrical" to the setup logic, and should stop o }, [serverUrl, roomId]); ``` -[Learn how the Effect lifecycle is different from the component's lifecycle.](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect) +[Apprenez en quoi le cycle de vie des Effets diffère de celui des composants](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect). --- -### My Effect does something visual, and I see a flicker before it runs {/*my-effect-does-something-visual-and-i-see-a-flicker-before-it-runs*/} +### Mon Effet fait un truc visuel, et l'affichage vacille avant son exécution {/*my-effect-does-something-visual-and-i-see-a-flicker-before-it-runs*/} -If your Effect must block the browser from [painting the screen,](/learn/render-and-commit#epilogue-browser-paint) replace `useEffect` with [`useLayoutEffect`](/reference/react/useLayoutEffect). Note that **this shouldn't be needed for the vast majority of Effects.** You'll only need this if it's crucial to run your Effect before the browser paint: for example, to measure and position a tooltip before the user sees it. +Si votre Effet doit empêcher le navigateur de [rafraîchir immédiatement l'affichage à l'écran](/learn/render-and-commit#epilogue-browser-paint), remplacez `useEffect` par [`useLayoutEffect`](/reference/react/useLayoutEffect). Remarquez que **ça ne devrait concerner qu'une toute petite minorité de cas**. Vous n'aurez besoin de ça que lorsqu'il est crucial que votre Effet soit exécuté avant le rafraîchissement par le navigateur ; par exemple, pour mesurer et positionner une infobulle avant que l'utilisateur ne la voie.