React • Créer des composants
Introduction
Section intitulée « Introduction »Dans cet article, nous allons reprendre le projet créé dans l’article précédent et l’améliorer en :
- créant des nouveaux composants ;
- utiliser TypeScript pour typer nos composants.
Composant ImageLink
Section intitulée « Composant ImageLink »RépertoireREACT-PROJECT
Répertoirenode_modules/
- …
Répertoirepublic/
- vite.svg
Répertoiresrc/
Répertoireassets/
- react.svg
- App.css
- App.tsx
- index.css
- main.tsx
- vite-end.d.ts
- .eslintrc.cjs
- .gitignore
- index.html
- package-lock.json
- package.json
- README.md
- tsconfig.app.json
- tsconfig.json
- tsconfig.node.json
- vite.config.ts
import { useState } from "react";import reactLogo from "./assets/react.svg";import viteLogo from "/vite.svg";import "./App.css";
function App() { const [count, setCount] = useState(0);
return ( <> <div> <a href="https://vitejs.dev" target="_blank"> <img src={viteLogo} className="logo" alt="Vite logo" /> </a> <a href="https://react.dev" target="_blank"> <img src={reactLogo} className="logo react" alt="React logo" /> </a> </div> <h1>Vite + React</h1> <div className="card"> <button onClick={() => setCount((count) => count + 1)}> count is {count} </button> <p> Edit <code>src/App.tsx</code> and save to test HMR </p> </div> <p className="read-the-docs"> Click on the Vite and React logos to learn more </p> </> );}
export default AppDans le composant App nous avons la possibilité d’extraire le lien <a> et son image <img> dans un nouveau composant.
Créons un nouveau fichier ImageLink.tsx.
RépertoireREACT-PROJECT
Répertoirenode_modules/
- …
Répertoirepublic/
- vite.svg
Répertoiresrc/
Répertoireassets/
- react.svg
- App.css
- App.tsx
- ImageLink.tsx
- index.css
- main.tsx
- vite-end.d.ts
- .eslintrc.cjs
- .gitignore
- index.html
- package-lock.json
- package.json
- README.md
- tsconfig.app.json
- tsconfig.json
- tsconfig.node.json
- vite.config.ts
En s’inspirant du composant App, commençons par écrire le code suivant.
function ImageLink() { return ( <></> );}
export default ImageLinkNotre composant va avoir besoin de paramètres :
- l’URL du lien ;
- la source de l’image ;
- le texte alternatif de l’image ;
- la classe CSS associée à l’image.
Cette liste de paramètre s’appelle Props. Les props doivent être regroupées dans un objet.
function ImageLink() {function ImageLink({href, imgSrc, imgAlt, imgClassName}) { return ( <></> );}
export default ImageLink;Remettons en place les balises <a> et <img> dans notre composant et utilisons les paramètres.
function ImageLink({href, imgSrc, imgAlt, imgClassName}) { return ( <></> <a href={href} target="_blank"> <img src={imgSrc} className={imgClassName} alt={imgAlt} /> </a> );}
export default ImageLinkNous pouvons maintenant utiliser notre nouveau composant dans App.
import { useState } from "react";import reactLogo from "./assets/react.svg";import viteLogo from "/vite.svg";import "./App.css";import ImageLink from "./ImageLink";
function App() { const [count, setCount] = useState(0);
return ( <> <div> <a href="https://vitejs.dev" target="_blank"> <img src={viteLogo} className="logo" alt="Vite logo" /> </a> <a href="https://react.dev" target="_blank"> <img src={reactLogo} className="logo react" alt="React logo" /> </a> <ImageLink href="https://vitejs.dev" imgAlt="Vite logo" imgClassName="logo" imgSrc={viteLogo} /> <ImageLink href="https://react.dev" imgAlt="React logo" imgClassName="logo react" imgSrc={reactLogo} /> </div> <h1>Vite + React</h1> <div className="card"> <button onClick={() => setCount((count) => count + 1)}> count is {count} </button> <p> Edit <code>src/App.tsx</code> and save to test HMR </p> </div> <p className="read-the-docs"> Click on the Vite and React logos to learn more </p> </> );}
export default AppNous avons remplacé les deux <img> par notre nouveau composant.
Sur le navigateur, la page fonctionne toujours de la même façon.
Néanmoins, si nous revenons sur le composant <ImageLink>
l’éditeur de code nous signale des erreurs sur les paramètres de notre fonction.
Binding element ‘href’ implicitly has an ‘any’ type.ts(7031)
En effet, nous avons développé en pur Javascript, sans rien typer. Nous allons maintenant voir comment modifier notre composant afin de profiter de TypeScript.
Typer le composant ImageLink
Section intitulée « Typer le composant ImageLink »Nous allons d’abord créer un type pour les propriétés de notre composant ImageLink.
type Props = { href: string, imgSrc: string, imgAlt: string, imgClassName: string,};
function ImageLink({href, imgSrc, imgAlt, imgClassName}: Props) { return ( <a href={href} target="_blank"> <img src={imgSrc} className={imgClassName} alt={imgAlt} /> </a> );}
export default ImageLink;Comment appliquer ce type à notre fonction ?
Première méthode : appliquer le type directement à l’objet en paramètre de la fonction.
type Props = { href: string, imgSrc: string, imgAlt: string, imgClassName: string,};
function ImageLink({href, imgSrc, imgAlt, imgClassName}: Props) { return ( <a href={href} target="_blank"> <img src={imgSrc} className={imgClassName} alt={imgAlt} /> </a> );}
export default ImageLink;Seconde méthode : profiter des types proposés par React.
import { FC } from "react";
type Props = { href: string, imgSrc: string, imgAlt: string, imgClassName: string,};
function ImageLink({href, imgSrc, imgAlt, imgClassName}: Props) {const ImageLink: FC<Props> = ({ href, imgSrc, imgAlt, imgClassName }) => { return ( <a href={href} target="_blank"> <img src={imgSrc} className={imgClassName} alt={imgAlt} /> </a> );}
export default ImageLink;- La fonction
ImageLink()a été convertie en Arrow function ; - Le type
FC(Function Component) a été appliqué à la fonctionImageLink(); - Le type
FCpeut recevoir le type d’argument, le type Props a été appliquéFC<Props>.
(alias) type FC<P = {}> = FunctionComponent<P>Represents the type of a function component. Can optionally receive a type argument that represents the props the component receives.Grace au type Props, nous avons défini toutes les propriétés attendues, ainsi que leur type.
Imaginons maintenant que nous souhaitions que le paramètre imgClassName soit optionnel.
Comment pouvons-nous faire ?
import { FC } from "react";
type Props = { href: string, imgSrc: string, imgAlt: string, imgClassName?: string,};
function ImageLink({href, imgSrc, imgAlt, imgClassName}: Props) {const ImageLink: FC<Props> = ({ href, imgSrc, imgAlt, imgClassName }) => { return ( <a href={href} target="_blank"> <img src={imgSrc} className={imgClassName} alt={imgAlt} /> </a> );}
export default ImageLink;Pour rendre un paramètre optionnel, il suffit d’ajouter ? à son type.
Ainsi en déclarant imgClassName?: string, imgClassName peut être undefined.
Composant PlusMinusButton
Section intitulée « Composant PlusMinusButton »Dans le composant App, nous souhaitons maintenant remplacer le bouton
par un nouveau composant permettant d’incrémenter et de décrémenter le compteur.
Créons un nouveau fichier PlusMinusButton.tsx.
RépertoireREACT-PROJECT
Répertoirenode_modules/
- …
Répertoirepublic/
- vite.svg
Répertoiresrc/
Répertoireassets/
- react.svg
- App.css
- App.tsx
- ImageLink.tsx
- index.css
- main.tsx
- PlusMinusButton.tsx
- vite-end.d.ts
- .eslintrc.cjs
- .gitignore
- index.html
- package-lock.json
- package.json
- README.md
- tsconfig.app.json
- tsconfig.json
- tsconfig.node.json
- vite.config.ts
import { FC } from "react";
type Props = {}
const PlusMinusButton: FC<Props> = () => { return ( <div> <button>+</button> <button>-</button> </div> );}
export default PlusMinusButton;Notre nouveau composant contient deux <button>.
Comment pouvons-nous faire pour indiquer au composant parent que le bouton + ou le bouton - a été cliqué ?
Prenons l’exemple de l’événement onClick du <button> dans le composant App :
<button onClick={() => setCount((count) => count + 1)}>Une fonction est attachée à onClick.
Cette fonction appelle setCount() lorsque l’événement click du <button> est levé.
Nous pouvons reproduire le même comportement dans notre composant PlusMinusButton.
import { FC } from "react";
type Props = { decrement: () => void, increment: () => void,}
const PlusMinusButton: FC<Props> = ({ decrement, increment }) => { return ( <div> <button>+</button> <button onClick={() => increment()}>+</button> <button>-</button> <button onClick={() => decrement()}>-</button> </div> );}
export default PlusMinusButton;Les fonctions decrement() et increment() ont été définies en tant que Props de notre composant.
Le type () => void signifie : fonction sans paramètre qui ne retourne rien (void).
La fonction decrement() est appelée au click du <button> -.
La fonction increment() est appelée au click du <button> +.
Nous pouvons maintenant utiliser notre nouveau composant dans App.
import { useState } from "react";import reactLogo from "./assets/react.svg";import viteLogo from "/vite.svg";import "./App.css";import ImageLink from "./ImageLink";import PlusMinusButton from "./PlusMinusButton";
function App() { const [count, setCount] = useState(0);
return ( <> <div> <a href="https://vitejs.dev" target="_blank"> <img src={viteLogo} className="logo" alt="Vite logo" /> </a> <a href="https://react.dev" target="_blank"> <img src={reactLogo} className="logo react" alt="React logo" /> </a> <ImageLink href="https://vitejs.dev" imgAlt="Vite logo" imgClassName="logo" imgSrc={viteLogo} /> <ImageLink href="https://react.dev" imgAlt="React logo" imgClassName="logo react" imgSrc={reactLogo} /> </div> <h1>Vite + React</h1> <div className="card"> <button onClick={() => setCount((count) => count + 1)}> count is {count} </button> <PlusMinusButton decrement={() => setCount((count) => count - 1)} increment={() => setCount((count) => count + 1)} /> <span>count is {count}</span> <p> Edit <code>src/App.tsx</code> and save to test HMR </p> </div> <p className="read-the-docs"> Click on the Vite and React logos to learn more </p> </> );}
export default App;Et voilà ! Nous avons mis en place un nouveau composant nous permettant de décrémenter et d’incrémenter notre compteur.
Conclusion
Section intitulée « Conclusion »Dans cet article, nous avons vu comment créer des composants avec des paramètres.
Ces paramètres s’appellent Props.
Avec l’aide de TypeScript, nous avons correctement typé ces Props.
Les Props du composant ImageLink ont une influence sur son rendu.
Ainsi le logo Vite et le logo React ont un rendu différent, bien qu’il s’agisse du même composant.
Le composant PlusMinusButton a des Props de type function.
Le composant parent App peut ainsi « réagir » lorsqu’une des fonctions est appelée.
Mais le rendu de PlusMinusButton ne change pas.
Bien entendu, un composant peut avoir à la fois des Props de type string, number ou array
et des Props de type function.