Introduction
Pour une récupération de données adéquate, il est courant d'utiliser des modèles de communication entre un parent et un enfant avec des callbacks. Nous explorerons l'API React Context et React Suspense pour résoudre plusieurs problèmes connus.
API React Context
L'API React Context est officielle depuis la version 16.3.0, alors qu'auparavant elle était expérimentale. Cette nouvelle API incite de nombreux développeurs à délaisser Redux au profit de l'API Context, qui permet de partager des données entre composants sans transmettre de props à chaque enfant.
Pour commencer, créez un contexte en créant un dossier nommé "contexts" et un fichier exemple "Issue.tsx". Importez les dépendances nécessaires et ajoutez les interfaces props, Issue et context :
import { FC, createContext, useState, useEffect, ReactElement, useCallback } from 'react'
import axios from 'axios'
export type Issue = {
number: number
title: string
url: string
state: string
}
interface Issue_Context {
issues: Issue[]
url: string
}
interface Props {
children: ReactElement
url: string
}
Ensuite, créez le contexte avec la fonction createContext et définissez la valeur à exporter :
export const IssueContext = createContext<Issue_Context>({
issues: [],
url: ''
})
Après avoir créé IssueContext, réalisez un composant pour recevoir des props, définir des états et effectuer la récupération avec useEffect. Ensuite, retournez IssueContext.Provider en spécifiant le contexte (valeur) à exporter :
const IssueProvider: FC<Props> = ({ children, url }) => {
// State
const [issues, setIssues] = useState<Issue[]>([])
const fetchIssues = useCallback(async () => {
const response = await axios(url)
if (response) {
setIssues(response.data)
}
}, [url])
// Effects
useEffect(() => {
fetchIssues()
}, [fetchIssues])
const context = {
issues,
url
}
return <IssueContext.Provider value={context}>{children}</IssueContext.Provider>
}
export default IssueProvider
Vous devez utiliser useCallback pour envelopper une fonction utilisée dans useEffect. Il est recommandé d'avoir une fonction async/await séparée plutôt que de l'inclure directement dans useEffect.
Après avoir récupéré les données et stocké celles-ci dans l'état "issues", ajoutez toutes les valeurs à exporter en tant que contexte. En rendant IssueContext.Provider, transmettez le contexte via les props et affichez les enfants du composant.
Pour consommer un contexte, il y a deux étapes :
- Encapsulez votre application avec le provider de contexte. Ce code peut être ajouté dans votre APP :
const App = () => {
return (
<IssueProvider url=
"https://api.github.com/repos/ContentPI/ContentPI/issues">
<Issues />
</IssueProvider>
)
}
Comme vous pouvez le constater, le composant Issues est encapsulé par IssueProvider. Ainsi, à l'intérieur du composant Issues, vous pouvez consommer le contexte et accéder aux valeurs des issues.
❗️ Ceci peut être source de confusion : si vous oubliez d'encapsuler les composants avec le provider, vous ne pourrez pas consommer le contexte à l'intérieur de vos composants. Le problème est qu'aucune erreur ne sera affichée, seulement des données indéfinies, ce qui rend la détection difficile.
Après avoir ajouté IssueProvider dans App.tsx, consommez le contexte dans le composant Issues en utilisant useContext :
// Dependencies
import { FC, useContext } from 'react'
// Contexts
import { IssueContext, Issue } from '../contexts/Issue'
const Issues: FC = () => {
// Here you consume your Context, and you can grab the issues value.
const { issues, url } = useContext(IssueContext)
return (
<>
<h1>ContentPI Issues from Context</h1>
{issues.map((issue: Issue) => (
<p key={`issue-${issue.number}`}>
<strong>#{issue.number}</strong> {' '}
<a href={`${url}/${issue.number}`}>{issue.title}</a> {' '}
{issue.state}
</p>
))}
</>
)
}
export default Issues
Et voici le résultat :

L'API Context est utile pour séparer les données de l'application et effectuer des récupérations. Elle peut servir à la thématisation ou au passage de fonctions, selon les besoins de l'application.
API Context vs Redux :
| API Context | Redux |
|---|---|
| Intégrée directement dans React, nécessite peu de configuration | Dépendance à ajouter, demande plus d'efforts pour l'intégration |
| Idéale pour des données statiques (peu mises à jour) | Convient pour des données statiques et dynamiques |
| Ajout de nouveaux contextes à créer à partir de zéro | Facilement extensible, ajout aisé de nouvelles données/actions |
| Débogage plus difficile | Outils de débogage fournis facilitant la tâche |
| Logique de l'interface utilisateur et logique de gestion d'état dans le même composant | Meilleure organisation du code avec logiques d'interface utilisateur et de gestion d'état distinctes |
React suspense (SWR)
React Suspense a été introduit dans React 16.6 et permet de suspendre le rendu des composants jusqu'à ce qu'une condition soit remplie. Il est possible d'afficher un composant de chargement ou autre comme solution de repli. Deux cas d'utilisation :
- Fractionnement de code : en attendant le téléchargement d'une partie de l'application lorsqu'un utilisateur souhaite y accéder.
- Récupération de données.
Dans les deux cas, un fallback (comme un spinner, un texte de chargement ou un squelette d'espace réservé) peut être affiché.
⚠️ Attention : React Suspense est encore expérimental et n'est pas recommandé pour une utilisation en production.
Stale-While-Revalidate (SWR) est un Hook React pour la récupération de données et une stratégie d'invalidation du cache HTTP. SWR renvoie d'abord les données du cache, envoie la demande de récupération, puis revient avec des données à jour. Il a été développé par Vercel, la société créatrice de Next.js.
Pour illustrer cela, nous allons créer un Pokédex en utilisant l'API : https://pokeapi.co.
Créez le répertoire "Pokemon" : src/components/Pokemon. Pour utiliser SWR, créez d'abord un fichier de récupération où seront effectuées les requêtes : fetcher.ts :
const fetcher = (url: string) => {
return fetch(url).then((response) => {
if (response.ok) {
return response.json()
}
return {
error: true
}
})
}
export default fetcher
Notez que nous retournons un objet contenant une erreur si la réponse échoue. En effet, il peut arriver que l'API renvoie une erreur 404, provoquant un dysfonctionnement de l'application. À présent, configurons SWR :
// Dependencies
import { SWRConfig } from 'swr'
// Components
import PokeContainer from './Pokemon/PokeContainer'
import fetcher from './Pokemon/fetcher'
// Styles
import { StyledPokedex, StyledTitle } from './Pokemon/Pokemon.styled'
const App = () => {
return (
<>
<StyledTitle>Pokedex</StyledTitle>
<SWRConfig
value={{
fetcher,
suspense: true,
}}
>
<StyledPokedex>
<PokeContainer />
</StyledPokedex>
</SWRConfig>
</>
)
}
Comme vous pouvez le constater, il faut encapsuler PokeContainer à l'intérieur de SWRConfig pour récupérer les données. PokeContainer sera notre composant parent dans lequel nous ajouterons notre premier Suspense :
import { FC, Suspense } from 'react'
import Pokedex from './Pokedex'
const PokeContainer: FC = () => {
return (
<Suspense fallback={<h2>Loading Pokedex...</h2>}>
<Pokedex />
</Suspense>
)
}
export default PokeContainer
Comme vous pouvez le constater, nous définissons un message de chargement pour notre Suspense : "Loading Pokedex...". Vous pouvez y afficher ce que vous voulez, des composants React ou du texte brut. Ensuite, nous avons notre composant Pokedex à l'intérieur de Suspense :
// Dependencies
import { FC, Suspense } from 'react'
import useSWR from 'swr'
// Components
import LoadingSkeleton from './LoadingSkeleton'
import Pokemon from './Pokemon'
import { StyledGrid } from './Pokemon.styled'
const Pokedex: FC = () => {
const { data: { results } } =
useSWR('https://pokeapi.co/api/v2/pokemon?limit=150')
return (
<>
{results.map((pokemon: { name: string }) => (
<Suspense fallback={<StyledGrid><LoadingSkeleton /></StyledGrid>}>
<Pokemon key={pokemon.name} pokemonName={pokemon.name} />
</Suspense>
))}
</>
)
}
export default Pokedex
Nous récupérons nos données pour la première fois en utilisant le hook useSWR. Comme vous pouvez le constater, nous récupérons les 150 premiers Pokémon, car je suis un fan de la première génération. Nous utilisons la fonction map pour afficher chaque Pokémon, en ajoutant un composant Suspense avec un LoadingSkeleton pour chacun. Nous transmettons "pokemonName" à notre composant, car la première récupération nous fournit uniquement le nom du Pokémon. Nous devons effectuer une autre recherche pour obtenir les données réelles du Pokémon (nom, types, puissance, etc.).
Enfin, notre composant Pokemon effectuera une récupération spécifique par le nom du Pokémon et affichera les données :
// Dependencies
import { FC } from 'react'
import useSWR from 'swr'
// Styles
import { StyledCard, StyledTypes, StyledType, StyledHeader } from './Pokemon.styled'
type Props = {
pokemonName: string
}
const Pokemon: FC<Props> = ({ pokemonName }) => {
const { data, error } =
useSWR(`https://pokeapi.co/api/v2/pokemon/${pokemonName}`)
// Do you remember the error we set on the fetcher?
if (error || data.error) {
return <div />
}
if (!data) {
return <div>Loading...</div>
}
const { id, name, sprites, types } = data
const pokemonTypes = types.map((pokemonType: any) =>
pokemonType.type.name)
return (
<StyledCard pokemonType={pokemonTypes[0]}>
<StyledHeader>
<h2>{name}</h2>
<div>#{id}</div>
</StyledHeader>
<img alt={name} src={sprites.front_default} />
<StyledTypes>
{pokemonTypes.map((pokemonType: string) => (
<StyledType key={pokemonType}>{pokemonType}</StyledType>
))}
</StyledTypes>
</StyledCard>
)
}
export default Pokemon
Dans ce composant, nous rassemblons toutes les données des Pokémon :
- id
- name
- sprites
- types
Comme vous pouvez le constater, j'utilise des "styled components", qui sont très pratiques. Si vous souhaitez connaître les styles que j'utilise pour Pokedex, voici le fichier Pokemon.styled.ts :
import styled from 'styled-components'
// Type colors
const type: any = {
bug: '#2ADAB1',
dark: '#636363',
dragon: '#E9B057',
electric: '#ffeb5b',
fairy: '#ffdbdb',
fighting: '#90a4b5',
fire: '#F7786B',
flying: '#E8DCB3',
ghost: '#755097',
grass: '#2ADAB1',
ground: '#dbd3a2',
ice: '#C8DDEA',
normal: '#ccc',
poison: '#cc89ff',
psychic: '#705548',
rock: '#b7b7b7',
steel: '#999',
water: '#58ABF6'
}
export const StyledPokedex = styled.div`
display: flex;
flex-wrap: wrap;
flex-flow: row wrap;
margin: 0 auto;
width: 90%;
&::after {
content: '';
flex: auto;
}
`
type Props = {
pokemonType: string
}
export const StyledCard = styled.div<Props>`
position: relative;
${({ pokemonType }) => `
background: ${type[pokemonType]} url(./pokeball.png) no-repeat;
background-size: 65%;
background-position: center;
`}
color: #000;
font-size: 13px;
border-radius: 20px;
margin: 5px;
width: 200px;
img {
margin-left: auto;
margin-right: auto;
display: block;
}
`
export const StyledTypes = styled.div`
display: flex;
margin-left: 6px;
margin-bottom: 8px;
`
export const StyledType = styled.span`
display: inline-block;
background-color: black;
border-radius: 20px;
font-weight: bold;
padding: 6px;
color: white;
margin-right: 3px;
opacity: 0.4;
text-transform: capitalize;
`
export const StyledHeader = styled.div`
display: flex;
justify-content: space-between;
width: 90%;
h2 {
margin-left: 10px;
margin-top: 5px;
color: white;
text-transform: capitalize;
}
div {
color: white;
font-size: 20px;
font-weight: bold;
margin-top: 5px;
}
`
export const StyledTitle = styled.h1`
text-align: center;
`
export const StyledGrid = styled.div`
display: flex;
flex-wrap: wrap;
flex-flow: row wrap;
div {
margin-right: 5px;
margin-bottom: 5px;
}
`
Enfin, notre composant LoadingSkeleton devrait ressembler à ceci :
import { FC } from 'react'
import Skeleton from 'react-loading-skeleton'
const LoadingSkeleton: FC = () => (
<div>
<Skeleton height={200} width={200} />
</div>
)
export default LoadingSkeleton
Cette bibliothèque est incroyable. Elle permet de créer des squelettes d'espaces réservés en attendant les données. Vous pouvez créer autant de formes que vous le souhaitez. Vous avez probablement déjà observé cet effet sur des sites tels que LinkedIn ou YouTube.
Voyons le résultat :

Ensuite, vous verrez les fallbacks Pokemon rendu par SkeletonLoading :

Pour afficher :

Plutôt sympa, n'est-ce pas ? Mais il y a autre chose à mentionner : comme indiqué précédemment, SWR récupère d'abord les données du cache, puis les revalide constamment pour vérifier s'il y a des mises à jour. Cela signifie qu'à chaque fois que les données changent, SWR effectue une nouvelle récupération pour confirmer si les anciennes données sont toujours valides ou doivent être remplacées. Vous pouvez observer cet effet en passant de l'onglet Pokédex à un autre, puis en revenant.
Actuellement, React Suspense n'a pas de modèle d'utilisation défini, ce qui signifie que vous pouvez trouver différentes manières de l'utiliser et qu'il n'y a pas encore de bonnes pratiques établies. J'ai trouvé que SWR est la manière la plus simple et compréhensible de travailler avec React Suspense, et je pense que c'est une bibliothèque très puissante qui peut être utilisée même sans Suspense.
Réconciliation
La plupart du temps, React est assez rapide par défaut et vous n'avez pas besoin d'effectuer d'autres optimisations pour améliorer les performances de votre application. React utilise différentes techniques pour optimiser l'affichage des composants à l'écran.
Chaque fois que React doit afficher un composant, il appelle sa méthode Render ainsi que celles de ses enfants de manière récursive, puis crée un arbre d'éléments dans le DOM. Lorsque l'état du composant change, ces méthodes sont à nouveau appelées sur les nœuds et React compare le résultat avec l'arborescence précédente des éléments. La bibliothèque est suffisamment intelligente pour déterminer le nombre minimum d'opérations nécessaires pour appliquer les modifications attendues à l'écran. Ce processus s'appelle la réconciliation et est géré de manière transparente par React.
React tente d'appliquer le plus petit nombre possible d'opérations sur le DOM, car c'est une opération coûteuse. Cependant, comparer deux arbres d'éléments n'est pas gratuit non plus, et React fait deux hypothèses pour réduire sa complexité :
- Si deux éléments ont un type différent, ils génèrent un arbre différent.
- Les développeurs peuvent utiliser des clés pour marquer les enfants comme stables à travers différents appels de rendu.
Le deuxième point est intéressant pour les développeurs, car il offre un outil pour aider React à afficher nos vues plus rapidement. Par défaut, React itère les deux listes d'enfants d'un nœud DOM en même temps et crée une mutation chaque fois qu'il y a une différence. Regardons quelques exemples. La conversion entre les deux arbres suivants fonctionnera bien lors de l'ajout d'un élément à la fin des enfants :
<ul>
<li>Carlos</li>
<li>Javier</li>
</ul>
<ul>
<li>Carlos</li>
<li>Javier</li>
<li>Emmanuel</li>
</ul>
On a deux arbres au début :
- Carlos
- Javier
Ensuite, React insérera un nouvel arbre :
- Emmanuel
Insérer un élément au début produit des performances inférieures s'il est implémenté naïvement. Si nous regardons l'exemple, cela fonctionne très mal lors de la conversion entre ces deux arbres :
<ul>
<li>Carlos</li>
<li>Javier</li>
</ul>
<ul>
<li>Emmanuel</li>
<li>Carlos</li>
<li>Javier</li>
</ul>
Chaque enfant sera modifié par React, plutôt que de réaliser qu'il peut conserver la structure des sous-arbres. Ce problème peut être résolu en utilisant l'attribut "key" pris en charge par React. Observons cela.
Les enfants ont des clés, et React utilise ces clés pour faire correspondre les enfants entre l'arbre précédent et le nouvel arbre. La mise à jour de l'arbre peut être optimisée en ajoutant une clé à notre exemple précédent :
<ul>
<li key="2018">Carlos</li>
<li key="2019">Javier</li>
</ul>
<ul>
<li key="2017">Emmanuel</li>
<li key="2018">Carlos</li>
<li key="2019">Javier</li>
</ul>
React peut désormais identifier que la clé :
- 2017 est nouvelle
- 2018 et 2019 ont été déplacées et ne sont pas nouvelles
Trouver une clé n'est pas compliqué. L'élément que vous affichez peut déjà avoir un identifiant unique. Ainsi, la clé peut provenir directement de vos données :
<li key={element.id}>{element.title}</li>
React est désormais capable de reconnaître que :
- la clé 2017 est nouvelle
- les clés 2018 et 2019 ont été déplacées et ne sont pas nouvelles.
Trouver une clé n'est pas difficile. L'élément que vous affichez peut déjà avoir un identifiant unique. Ainsi, la clé peut provenir directement de vos données.
Techniques d'optimisation
L'utilisation de la version de développement de React est très utile pour coder et déboguer car elle fournit toutes les informations nécessaires pour résoudre les différents problèmes. Cependant, tous les contrôles et avertissements ont un coût que nous souhaitons éviter en production. Par conséquent, la première optimisation que nous devrions apporter à nos applications est la construction du bundle, en définissant la variable d'environnement NODE_ENV en mode production. Cela peut être facilement réalisé avec webpack, en utilisant la méthode suivante :
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
Pour obtenir les meilleures performances, nous ne voulons pas seulement créer le bundle avec l'indicateur de production activé, mais nous voulons également diviser nos bundles en deux : un pour notre application et un autre pour les node_modules.
optimization: {
splitChunks: {
cacheGroups: {
default: false,
commons: {
test: /node_modules/,
name: 'vendor',
chunks: 'all'
}
}
}
}
Webpack 4 dispose de deux modes :
- développement
- production
Le mode production est activé par défaut. Ainsi, le code est minifié et compressé lors de la compilation des bundles. Pour spécifier le mode production, vous pouvez utiliser le bloc de code suivant :
{
mode: process.env.NODE_ENV === 'production' ? 'production' :
'development',
}
Avec cette configuration de webpack, nous allons obtenir des bundles très optimisés, un pour nos fournisseurs et un pour l'application réelle.
Les anti-patterns
Les performances passent aussi par éviter de faire n'importe quoi !
1. Initialisation de l'état à l'aide des propriétés
Dans cette section, nous allons voir que l'initialisation de l'état à l'aide des propriétés reçues du parent est généralement considérée comme un anti-modèle. Cependant, nous pourrions encore décider de l'utiliser une fois que nous aurons clairement identifié les problèmes associés à cette approche.
L'une des meilleures façons d'apprendre est d'examiner du code. Nous allons donc commencer par créer un composant simple contenant un bouton qui incrémente un compteur.
import { FC, useState } from 'react'
type Props = {
count: number
}
const Counter: FC<Props> = (props) => {}
export default Counter
Maintenant, définissons notre state:
const [state, setState] = useState<any>(props.count)
L'implémentation du gestionnaire de clics est assez simple :
const handleClick = () => {
setState({ count: state.count + 1 })
}
Enfin:
return (
<div>
{state.count}
<button onClick={handleClick}>+</button>
</div>
)
Maintenant, rendons ce composant, en passant "1" comme propriété :
<Counter count={1} />
Le code fonctionne comme prévu : chaque clic sur le bouton + incrémente la valeur actuelle. Mais quel est le problème ?
Il y a deux erreurs principales :
- Nous avons une source de vérité dupliquée.
- Si la propriété count transmise au composant change, l'état n'est pas mis à jour.
Si nous examinons l'élément Counter à l'aide des outils de développement React DevTools :
<Counter>
Props
count: 1
State
count: 1
Cela ne permet pas de connaître la valeur actuelle et fiable à utiliser dans le composant et à afficher à l'utilisateur.
Pire encore, cliquer sur + une fois fait diverger les valeurs. Un exemple de cette divergence est illustré dans le code suivant :
<Counter>
Props
count: 1
State
count: 2
À ce stade, nous pouvons supposer que la deuxième valeur représente le nombre actuel, mais cela n'est pas explicite et peut entraîner des comportements inattendus ou des valeurs incorrectes dans l'arborescence.
Le deuxième problème se concentre sur la façon dont la classe est créée et instanciée par React. La fonction useState du composant n'est appelée qu'une seule fois lors de la création du composant. Dans notre composant, nous lisons la valeur de la propriété et la stockons dans l'état. Si la valeur de cette propriété change pendant le cycle de vie de l'application, le composant n'utilisera jamais la nouvelle valeur car il a déjà été initialisé. Cela place le composant dans un état incohérent, qui est suboptimal et difficile à déboguer.
Que se passe-t-il si nous voulons vraiment utiliser la valeur de la prop pour initialiser le composant, et que nous sommes certains que la valeur ne changera pas à l'avenir ?
Dans ce cas, il est préférable de le rendre explicite et de donner à la propriété un nom qui clarifie vos intentions, tel que initialCount. Par exemple :
type Props = {
initialCount: number
}
const Counter: FC<Props> = (props) => {
const [count, setState] = useState<any>(props.initialCount)
...
}
Si nous l'utilisons ainsi, il est clair que le parent n'a qu'un moyen d'initialiser le compteur :
<Counter initialCount={1} />
2. Utilisation de l'index comme clé
La propriété key identifie de manière unique un élément dans le DOM, et React l'utilise pour vérifier si l'élément est nouveau ou s'il doit être mis à jour lorsque les propriétés ou l'état du composant changent. Utiliser des clés est toujours une bonne idée, et si vous ne le faites pas, React émet un avertissement dans la console (en mode développement). Cependant, il ne s'agit pas simplement d'utiliser une clé ; parfois, la valeur que nous décidons d'utiliser comme clé peut faire la différence. En effet, l'utilisation de la mauvaise clé peut nous donner des comportements inattendus dans certains cas. Dans cette section, nous verrons un de ces cas.
import { FC, useState } from 'react'
const List: FC = () => {
}
export default List
Notre state :
const [items, setItems] = useState(['foo', 'bar'])
L'implémentation du gestionnaire de clic est légèrement différente de la précédente car dans ce cas, nous devons insérer un nouvel élément en haut de la liste :
const handleClick = () => {
const newItems = items.slice()
newItems.unshift('baz')
setItems(newItems)
}
Enfin:
return (
<div>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
<button onClick={handleClick}>+</button>
</div>
)
Si vous exécutez le composant dans le navigateur, vous ne rencontrerez aucun problème ; cliquer sur le bouton insère un nouvel élément en haut de la liste. Cependant, faisons une expérience. Modifions la fonction render de la manière suivante, en ajoutant un champ de saisie à côté de chaque élément. Nous utilisons ensuite un champ de saisie car nous pouvons modifier son contenu, ce qui facilite la compréhension du problème :
return (
<div>
<ul>
{items.map((item, index) => (
<li key={index}>
{item}
<input type="text" />
</li>
))}
</ul>
<button onClick={handleClick}>+</button>
</div>
)
Si nous exécutons à nouveau ce composant dans le navigateur, copions les valeurs des éléments dans les champs de saisie, puis cliquons sur +, nous obtiendrons un comportement inattendu. Comme le montre la capture d'écran suivante, les éléments se décalent alors que les champs de saisie restent à la même position, de sorte que leur valeur ne correspond plus à la valeur des éléments :

Si nous exécutons le composant, cliquons sur +, et vérifions la console, nous devrions avoir toutes les réponses dont nous avons besoin. Ce que nous pouvons voir, c'est que React, au lieu d'insérer le nouvel élément en haut, échange le texte des deux éléments existants et insère le dernier élément en bas comme s'il était nouveau. La raison en est que nous utilisons l'index de la fonction map comme clé.
C'est un schéma très courant car on pourrait penser que fournir n'importe quelle clé est toujours la meilleure solution, mais ce n'est pas du tout le cas. La clé doit être unique et stable, identifiant un et un seul élément. Pour résoudre ce problème, nous pouvons, par exemple, utiliser la valeur de l'élément si nous nous attendons à ce qu'elle ne se répète pas dans la liste, ou créer un identifiant unique.
3. Propagation des propriétés sur les éléments DOM
Il existe une pratique courante qui a récemment été décrite comme un anti-modèle par Dan Abramov ; elle déclenche également un avertissement dans la console lorsque vous l'utilisez dans votre application React. C'est une technique largement utilisée dans la communauté et je l'ai personnellement vue plusieurs fois dans des projets réels. Nous avons généralement recours à la technique de la propagation des propriétés sur les éléments pour éviter d'écrire chacun d'entre eux manuellement, comme illustré ci-dessous :
<Component {...props} />
Cela fonctionne très bien et est transpilé dans le code suivant par Babel :
_jsx(Component, props)
Cependant, lorsque nous diffusons des propriétés sur un élément DOM, nous courons le risque d'ajouter des attributs HTML inconnus, ce qui est une mauvaise pratique. Le problème n'est pas seulement lié à l'opérateur de diffusion ; le passage de propriétés non standard une par une entraîne les mêmes problèmes et avertissements. Étant donné que l'opérateur de diffusion masque les propriétés individuelles que nous diffusons, il est encore plus difficile de comprendre ce que nous transmettons à l'élément.
Pour voir l'avertissement dans la console, une opération de base que nous pouvons effectuer est de rendre le composant suivant :
const Spread = () => <div foo="bar" />
Console:
Unknown prop `foo` on <div> tag. Remove this prop from the element
Dans ce cas, comme nous l'avons mentionné, il est facile de déterminer quel attribut nous transmettons et de le supprimer. Cependant, si nous utilisons l'opérateur de diffusion, comme dans l'exemple suivant, nous ne pouvons pas contrôler quelles propriétés sont transmises depuis le parent :
const Spread = props => <div {...props} />;
Si nous utilisons le composant de la manière suivante, il n'y a pas de problèmes :
<Spread className="foo" />
Ceci, cependant, n'est pas le cas si nous faisons quelque chose comme ce qui suit. React se plaint car nous appliquons un attribut non standard à l'élément DOM :
<Spread foo="bar" className="baz" />
Une solution que nous pouvons utiliser pour résoudre ce problème consiste à créer une propriété appelée qui contient explicitement les propriétés DOM valides que nous pouvons diffuser en toute sécurité au composant. Par exemple, nous pouvons modifier le composant de la manière suivante :
const Spread = props => <div {...props.domProps} />
Nous pouvons alors l'utiliser comme suit :
<Spread foo="bar" domProps={{ className: 'baz' }} />
Comme nous l'avons vu à plusieurs reprises avec React, il est toujours bon d'être explicite.





