diff --git a/app/components/syntax/Callout.tsx b/app/components/syntax/Callout.tsx index 9a73e2d..e933547 100644 --- a/app/components/syntax/Callout.tsx +++ b/app/components/syntax/Callout.tsx @@ -29,10 +29,12 @@ export function Callout({ title, children, type = "note", + collapsible = false, }: { title: string; children: React.ReactNode; type?: keyof typeof styles; + collapsible?: boolean; }) { let IconComponent = icons[type]; diff --git a/app/data/docs/react/hooks/page.md b/app/data/docs/react/hooks/page.md new file mode 100644 index 0000000..e06476a --- /dev/null +++ b/app/data/docs/react/hooks/page.md @@ -0,0 +1,364 @@ +--- +title: Les hooks de React +description: Découvre les hooks de React, une fonctionnalité qui te permet de gérer le state et le cycle de vie de tes composants fonctionnels. +tags: [Frontend, React, JavaScript, TypeScript, Bibliothèque, Interface utilisateur (UI)] +--- + +Ça y est, on rentre dans le vif du sujet avec les **hooks** de React ! + +On en a déjà parlé un peu dans l'article précédent _(notamment avec le hook `useState` pour déclarer un state)_, mais on va maintenant les aborder en détail. + +## 🎣 Qu'est-ce qu'un hook ? + +Tu te souviens du charabia dans l'article précédent ? + +> Un **hook** en React est une fonction qui permet d'exploiter les fonctionnalités de React dans un composant fonctionnel _(fonction)_. + +Essayons de comprendre cette phrase un peu plus en détail en prenant l'origine des composants React. + +Historiquement, on utilisait des **classes** pour déclarer des composants React plutôt que des **fonctions**. +C'était pas mal, mais ça devenait vite compliqué à gérer, notamment pour partager de la logique entre plusieurs composants _(comme le state par exemple)_. + +Pour te donner un aperçu, voici à quoi ressemblait un composant de classe avec les trois étapes du cycle de vie : + +{% tabs defaultSelectedTab="jsx" %} + +{% tab value="jsx" label="JSX" %} + +```jsx +import React from "react"; + +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state = { count: 0 }; + } + + componentDidMount() { + console.log("Component mounted"); + } + + componentDidUpdate() { + console.log("Component updated"); + } + + componentWillUnmount() { + console.log("Component unmounted"); + } + + render() { + return
{this.state.count}
; + } +} +``` + +{% /tab %} + +{% tab value="tsx" label="TSX" %} + +```tsx +import React from 'react'; + +type MyComponentState = { + count: number; +}; + +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state: MyComponentState = { count: 0 }; + } + + componentDidMount() { + console.log('Component mounted'); + } + + componentDidUpdate() { + console.log('Component updated'); + } + + componentWillUnmount() { + console.log('Component unmounted'); + } + + render() { + return
{this.state.count}
; + } +} +``` + +{% /tab %} + +{% /tabs %} + +Comme dirait l'un de mes chers confrères jury : + +> C'est pas téros 😕 + +Mais si tu as bien fait attention, tu as pu remarquer trois méthodes _(on ne prend pas le constructor en compte)_ qui sont appelées à des moments précis du cycle de vie du composant : + +- `componentDidMount` : appelée après le premier rendu du composant +- `componentDidUpdate` : appelée après chaque mise à jour du composant +- `componentWillUnmount` : appelée avant la suppression du composant + +Seulement, comment on peut faire pour gérer ces étapes avec des composants fonctionnels ? +Et bien c'est là qu'interviennent les **hooks** ! + +Avant de te montrer **comment** reproduire ces étapes avec des **hooks**, voici les **principaux hooks** de base que tu vas **très souvent** utiliser : + +- `useState` : pour déclarer un **state** +- `useEffect` : pour gérer le **cycle de vie** d'un composant _(on en parle juste après !)_ + +Il en existe d'autres bien entendu, mais ces deux-là sont les plus utilisés. +On reviendra sur les autres hooks "basiques" un peu plus tard 😉 + +## useEffect, ou la machinerie du cycle de vie + +Ce hook.. il est **tellement** puissant ! +Mais il est surtout très mal compris par les débutants _(et même parfois par les confirmés)_. + +Il faut dire que Facebook n'a pas aidé en le nommant `useEffect`, surtout que tu vas voir : c'est un couteau suisse ce machin 😅 + +Pour faire court : `useEffect` permet de gérer le cycle de vie d'un composant fonctionnel _(comme `componentDidMount`, `componentDidUpdate` et `componentWillUnmount` pour les composants de classe)_. + +Oui. Il fait tout. Tout seul. +Tu comprends pourquoi je dis "couteau suisse" ? 😏 + +Alors sur le papier c'est top, mais maintenant je te laisse t'amuser à comprendre comment ça fonctionne 😇 + +{% tabs defaultSelectedTab="1" %} + +{% tab value="1" label="Écriture #1" %} + +```jsx +React.useEffect(() => { + // ... +}, []); +``` + +{% /tab %} + +{% tab value="2" label="Écriture #2" %} + +```jsx +React.useEffect(() => { + // ... +}, [props.uneProp]); +``` + +{% /tab %} + +{% tab value="3" label="Écriture #3" %} + +```jsx +React.useEffect(() => { + // ... +}); +``` + +{% /tab %} + +{% /tabs %} + +Pas cool, hein ? 😂 +Et bien dans ces exemples, on a trois manières d'écrire un useEffect : + +1. Le hook est exécuté une seule fois, après le premier rendu du composant +2. Le hook est exécuté à chaque mise à jour du composant +3. Le hook est exécuté à chaque mise à jour du composant, mais seulement si la propriété `uneProp` de `props` a changé + +{% callout type="note" title="`useEffect` et les mises à jour du composant" %} + +Alors quand je dis "le hook est exécuté à chaque mise à jour du composant", il faut également prendre en compte qu'il est également exécuté après le premier rendu du composant. + +{% /callout %} + +Mais alors, comment on fait pour gérer ces étapes avec des composants fonctionnels ? +Si tu n'as pas vu la différence entre les trois écritures, tu remarqueras que c'est le deuxième argument de useEffect qui fait la différence. + +Le premier argument lui, est une fonction synchrone _(pas le droit de la rendre asynchrone !)_. On mettra dedans tout ce qu'on veut exécuter lors de l'appel du hook. + +Le deuxième argument est un tableau de dépendances. +Selon ce tableau, le hook sera exécuté à des moments différents du cycle de vie du composant. + +### ⚙️ ComponentDidMount + +```jsx +React.useEffect(() => { + // ... +}, []); +``` + +Le tableau de dépendances est vide, on sous-entend que le hook ne dépend d'aucune variable et sera exécuté une seule fois. +On peut donc dire que c'est l'équivalent de `componentDidMount` pour les composants de classe. + +### 🔧 ComponentDidUpdate + +```jsx +React.useEffect(() => { + // ... +}); +``` + +Ici, le tableau de dépendances est absent _(et tout va bien, il est optionnel !)_. +Le hook sera exécuté à chaque mise à jour du composant, ainsi que lors du premier rendu. + +```jsx +React.useEffect(() => { + // ... +}, [props.uneProp]); +``` + +Dans ce cas, le tableau de dépendances contient la propriété `uneProp` de `props`. +Le hook sera exécuté à chaque mise à jour du composant _(ainsi qu'au montage)_, mais seulement si la propriété `uneProp` a changé. + +### 🗑️ ComponentWillUnmount + +Et là, tu te dis : "Mais comment je fais pour gérer le démontage du composant ?". +Hehehe, c'est là que ça devient intéressant 😏 + +```jsx +React.useEffect(() => { + // ... + return () => { + // ... + }; +}, []); +``` + +Tu as vu ce petit `return` ? Et bien, c'est notre équivalent de `componentWillUnmount` pour les composants de classe ! + +Dans cette fonction de retour, on mettra tout ce qu'on veut exécuter avant la suppression du composant. + +### 📦 Les dépendances + +Tu as pu voir que le deuxième argument de `useEffect` est un tableau de dépendances. +Ce tableau est très important, car il permet de gérer les mises à jour du composant. + +L'idée ici, c'est **d'optimiser** le rendu du composant en évitant de déclencher des mises à jour inutiles. +Pour éviter que React se dise "Tiens, il y a eu un changement, je vais re-rendre le composant", on va lui dire "Non, non, il n'y a pas eu de changement sur la prop que je t'ai fourni, tu peux rester tranquille". + +### 🤓 Exemple concret + +Allez, mettons un peu ce qu'on voit de voir en pratique ! + +{% tabs defaultSelectedTab="jsx" %} + +{% tab value="jsx" label="JSX" %} + +```jsx +import React from "react"; + +export const Counter = () => { + const [count, setCount] = React.useState(0); + + React.useEffect(() => { + console.log("Component mounted"); + + return () => { + console.log("Component unmounted"); + }; + }, []); + + React.useEffect(() => { + console.log("Component updated"); + }); + + const increment = () => setCount(count + 1); + + return ; +}; +``` + +{% /tab %} + +{% tab value="tsx" label="TSX" %} + +```tsx +import React from "react"; + +export const Counter = () => { + const [count, setCount] = React.useState(0); + + React.useEffect(() => { + console.log("Component mounted"); + + return () => { + console.log("Component unmounted"); + }; + }, []); + + React.useEffect(() => { + console.log("Component updated"); + }); + + const increment = () => setCount(count + 1); + + return ; +}; +``` + +{% /tab %} + +{% /tabs %} + +### 🔢 On revient sur le cycle de vie ! + +Et... stoooop ! +On revient sur le cycle de vie d'un composant maintenant qu'on a vu `useEffect` en action ! + +Je vais te donner un exemple de code supplémentaire et tu vas devoir deviner l'ordre d'apparition des messages dans la console. + +```jsx +import React from "react"; + +export const MyComponent = () => { + React.useEffect(() => { + console.log("1"); + }); + + console.log("2"); + + React.useEffect(() => { + console.log("3"); + }, []); + + const logInRender = () => { + console.log("4"); + return null; + }; + + return
{logInRender()}
; +}; +``` + +Voici les possibilités : + +{% callout type="question" title="Quel est l'ordre d'apparition des messages dans la console ?" %} + +- **A** - 4, 2, 1, 3 +- **B** - 2, 4, 1, 3 +- **C** - 1, 2, 3, 4 +- **D** - La réponse D + +{% /callout %} + +## 🧩 Les autres hooks + +On a vu les deux hooks les plus utilisés, mais il en existe d'autres qui peuvent être très utiles dans certaines situations. + +Par exemple, on a : + +- `useContext` : pour accéder à un contexte +- `useReducer` : pour gérer un state complexe +- `useCallback` : pour éviter les re-rendus inutiles +- `useMemo` : pour éviter les calculs inutiles +- `useRef` : pour accéder à un élément du DOM + +Ne t'inquiète pas, on va les voir plus tard car le hook `useEffect` est déjà bien assez complexe à comprendre pour le moment 😅 + +## Conclusion + +Et voilà, tu as maintenant toutes les clés en main pour gérer le cycle de vie de tes composants fonctionnels avec les hooks de React ! + +Vraiment, même si les autres hooks restent importants _(voire obligatoires dans certains contextes)_, tu as déjà de quoi faire de bons composants avec seulement ces deux là 😁 diff --git a/app/data/docs/react/jsx/page.md b/app/data/docs/react/jsx/page.md new file mode 100644 index 0000000..043ff4f --- /dev/null +++ b/app/data/docs/react/jsx/page.md @@ -0,0 +1,317 @@ +--- +title: La syntaxe JSX de React +description: Découvrons la syntaxe JSX, un langage de balisage utilisé par React pour décrire l'interface utilisateur. +tags: [Frontend, React, JavaScript, TypeScript, Bibliothèque, Interface utilisateur (UI)] +--- + +Avant de commencer à parler des composants React, découvrons tranquillement la syntaxe **JSX**. + +Le **JSX** est un sucre syntaxique _(une syntaxe plus lisible et plus simple que le JavaScript pur)_ qui permet de décrire l'interface utilisateur _(UI)_ de notre application. + +Le sigle en lui-même signifie **JavaScript XML**, dans le sens où l'on va retrouver une syntaxe proche du **XML** _(eXtensible Markup Language)_ qui est un langage de balisage _(comme le **HTML**)_. + +## 🔍 Différences entre HTML et JSX + +Et oui, le **JSX** ressemble beaucoup au **HTML** et c'est normal ! +C'est l'objectif premier de **React** : rendre la création d'interfaces utilisateur _(UI)_ plus simple et plus intuitive. + +Cependant il ne faut pas oublier que le **JSX** n'est pas du **HTML**, mais du **JavaScript**. + +Pour faire plus simple, voici un élément **HTML** et son équivalent avec React _(avec et sans JSX)_ : + +{% tabs defaultSelectedTab="html" %} + +{% tab value="html" label="HTML" %} + +```html + +``` + +{% /tab %} + +{% tab value="react-no-jsx" label="React sans JSX" %} + +```js +React.createElement("button", { className: "button" }, "Clique moi !"); +``` + +{% /tab %} + +{% tab value="jsx" label="React avec JSX" %} + +```jsx + +``` + +{% /tab %} + +{% /tabs %} + +Comme tu peux le constater, la différence entre le **JSX** et le **HTML** est minime. +Il y a toutefois des différences, comme certains mots réservés _(comme `class` qui devient `className`)_ ou encore la manière de déclarer des événements _(comme `onclick` qui devient `onClick`)_. + +Par contre si on regarde la différence entre le **JSX** et le **JavaScript pur** _(en utilisant React quand même)_, on voit bien que le **JSX** est beaucoup plus lisible et plus simple à écrire. + +Là où c'est encore plus flagrant, c'est quand on commence à imbriquer des éléments _(comme des composants React par exemple)_ ! + +{% tabs defaultSelectedTab="react-no-jsx" %} + +{% tab value="react-no-jsx" label="React sans JSX" %} + +```js +React.createElement( + React.Fragment, + null, + React.createElement("h2", null, "Formulaire de contact"), + React.createElement( + "form", + { onSubmit: handleSubmit }, + React.createElement( + "fieldset", + null, + React.createElement("label", { htmlFor: "lastname" }, "Nom"), + React.createElement("input", { type: "text", name: "lastname", id: "lastname", required: true }), + ), + React.createElement( + "fieldset", + null, + React.createElement("label", { htmlFor: "email" }, "Email"), + React.createElement("input", { type: "email", name: "email", id: "email", required: true }), + ), + React.createElement( + "fieldset", + null, + React.createElement("label", { htmlFor: "message" }, "Message"), + React.createElement("textarea", { name: "message", id: "message", required: true }), + ), + React.createElement( + "fieldset", + null, + React.createElement( + "label", + { htmlFor: "gdpr" }, + React.createElement("input", { type: "checkbox", name: "gdpr", id: "gdpr", required: true }), + "J'accepte que mes données soient utilisées pour me recontacter", + ), + ), + React.createElement("button", { type: "submit" }, "Envoyer"), + ), +); +``` + +{% /tab %} + +{% tab value="jsx" label="React avec JSX" %} + +```jsx + +

Formulaire de contact

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+
+``` + +{% /tab %} + +{% /tabs %} + +Et bien même si le code final est **identique**, le **JSX** apporte une lisibilité et une simplicité d'écriture qui est très appréciable. Pas mal non ? 😄 + +Et donc oui ! En faisant du **JSX**, on fait en réalité du **JavaScript** et **pas du HTML** ! + +{% callout type="note" title="Importation de React et ses exports" %} + +Au sein de ses pages, tu verras **toujours** que j'importe le contenu de React en intégralité _(comme `import React from 'react';`)_. + +Dans la réalité, on va destructurer les exports de React pour n'importer que ce dont on a besoin. + +Cependant, pour te donner l'information d'où provient chaque élément, je préfère importer React en intégralité et que tu puisses visualiser les éléments de React utilisés avec leur provenance. + +{% /callout %} + +## 🧩 Intégration de JavaScript dans le JSX + +Mais l'un des autres avantages du **JSX** est la possibilité d'ajouter du JavaScript directement dans le code ! + +Pour pouvoir ajouter du JavaScript dans le **JSX**, il suffit d'entourer le code JavaScript avec des accolades `{}`. +C'est un peu comme si on "ouvrait un portail" pour insérer du JavaScript dans notre code **JSX**. + +### 📦 Variables et fonctions + +Par exemple, si tu veux afficher une variable dans ton JSX, tu peux le faire directement : + +```jsx +const name = "Jean Dupont"; + +return

Bonjour {name} !

; +``` + +Et si tu veux appeler une fonction, c'est tout aussi simple : + +```jsx +const sayHello = () => "Bonjour !"; + +return

{sayHello()}

; +``` + +### 📝 Expressions + +Tu peux également ajouter des expressions _(comme des conditions ternaires par exemple)_ : + +```jsx +const age = 18; + +return

{age >= 18 ? "Majeur" : "Mineur"}

; +``` + +Mais tu peux aussi faire un **affichage conditionnel** de manière très simple : + +```jsx +const isLogged = false; + +return ( +
+ {isLogged &&

Bienvenue sur notre site !

} + {!isLogged &&

Connectez-vous pour accéder à notre site

} +
+); +``` + +### 🔄️ Boucles + +Maintenant imagine que tu souhaites créer une interface qui liste des éléments provenant d'un tableau. + +```jsx +const fruits = ["pomme", "banane", "fraise"]; +``` + +Dans un premier temps, on va revoir très rapidement comment on peut parser un tableau en JavaScript : + +- `for` : + + ```js + for (let i = 0; i < fruits.length; i++) { + console.log(fruits[i]); + } + ``` + +- `forEach` : + + ```js + fruits.forEach((fruit) => { + console.log(fruit); + }); + ``` + +- `map` : + ```js + fruits.map((fruit) => { + console.log(fruit); + }); + ``` + +En soit, toutes ces méthodes sont très bien et font ce qu'on leur demande sans souci. +Cependant, React ne va pas forcément aimer ça sauf pour `map`. + +La raison est simple : +React a besoin qu'on lui **retourne un élément** _(ou un tableau d'éléments)_ pour pouvoir les afficher. + +Alors avec des `console.log` on ne va pas aller loin, mais si au lieu de retourner un `console.log` on retournait un élément **JSX** ? 🤔 + +```jsx +const fruits = ["pomme", "banane", "fraise"]; + +return ( + +); +``` + +[Voir l'exemple sur PlayCode](https://playcode.io/1940876) + +Et là : **BAM** ! 💥 +Tu viens de créer une liste de fruits en utilisant un tableau de fruits. + +Mais par contre... + +{% callout type="question" title="C'est quoi ce `key` qui vient d'apparaître ?" %} + +La `key` est une propriété spéciale que React utilise pour identifier chaque élément de manière unique. +Cela permet à React de savoir quel élément a été ajouté, modifié ou supprimé. + +Il est **obligatoire** d'avoir une `key` **unique** pour chaque élément d'une liste. +Si tu listes des éléments qui ont un identifiant unique _(comme l'`id` qu'on aura dans nos données stockées dans une base de données par exemple)_, tu peux utiliser cet identifiant comme `key`. + +{% /callout %} + +## 📦 Les props + +Les **props** _(ou propriétés)_ sont des arguments que l'on peut passer à un composant React. +Je ne vais pas trop rentrer dans les détails ici, car on va les voir dans l'article d'après ! + +Mais pour te donner un aperçu, voici comment on peut passer des **props** à un composant : + +{% tabs defaultSelectedTab="jsx" %} + +{% tab value="jsx" label="JSX" %} + +```jsx +const Button = (props) => { + return ; +}; +``` + +{% /tab %} + +{% tab value="tsx" label="TSX" %} + +```tsx +type ButtonProps = { + onClick: () => void; + children: React.ReactNode; +}; + +const Button = (props: ButtonProps) => { + return ; +}; +``` + +{% /tab %} + +{% /tabs %} + +Ici, on a un composant `Button` qui prend deux **props** : `onClick` et `children`. +`onClick` est une fonction qui sera appelée lorsqu'on cliquera sur le bouton, et `children` est tout ce qui se trouve entre les balises ouvrante et fermante du composant. + +## Conclusion + +Alors, plutôt cool le **JSX** non ? 😎 + +Même si cette syntaxe rebute certains développeurs _(souvent ils se la jouent puristes, mais chuuuuut 🤫)_, elle est toutefois très appréciée pour sa simplicité et sa lisibilité. +Question de goût après tout ! diff --git a/app/data/docs/react/premier-composant/page.md b/app/data/docs/react/premier-composant/page.md new file mode 100644 index 0000000..cf36df2 --- /dev/null +++ b/app/data/docs/react/premier-composant/page.md @@ -0,0 +1,238 @@ +--- +title: Premier composant React +description: Voyons ensemble comment notre premier composant React ! +tags: [Frontend, React, JavaScript, TypeScript, Bibliothèque, Interface utilisateur (UI)] +--- + +Rentrons maintenant dans le vif du sujet en créant notre premier composant React ! +Dans cet article, on va faire un composant très simple : un **dumb component** qui sera tout simplement un bouton. + +{% callout type="question" title="Un... Dumb component ?" %} + +Un **dumb component** est un composant React qui ne contient pas de logique. +Enfin si, il peut en contenir _(mais rien de foufou)_, son rôle est de simplement afficher des données. + +En gros : + +- Il ne fait que de l'affichage +- Il ne fait pas de calculs +- Il ne fait pas de requêtes HTTP + +{% /callout %} + +## ⚙️ Syntaxes pour créer un composant + +Il existe plusieurs façons de créer un composant React. + +La plus simple est de créer une fonction qui retourne du JSX, mais il est aussi possible de créer une classe qui hérite `React.Component`. + +Cependant, on ne va pas s'attarder sur la déclaration de composants dans des classes. + +Pourquoi ? Déjà parce que la documentation officielle recommande désormais de définir des composants en tant que fonctions, mais surtout parce que c'est plus simple et plus lisible ! 🚀 + +## 👷 Création du composant + +Allez c'est parti, occupons-nous de notre premier composant React ! + +```jsx showLineNumbers +import React from "react"; + +export function Button() { + return ; +} +``` + +... et c'est tout ! 🎉 +On a créé notre premier composant React, c'est pas beau ça ? + +Bon évidemment, il reste extrêmement sommaire et n'a pas d'intérêt à l'heure actuelle, mais c'est un bon début ! + +Avant d'aller plus loin, décortiquons un peu ce code : + +- On importe `React` depuis la bibliothèque `React` _(ligne 1)_ +- On crée une fonction `Button` _(lignes 3 à 5)_ + - Cette fonction retourne un élément JSX `` + +## 🧩 Utilisation du composant + +C'est bien beau d'avoir un composant, mais maintenant il faut l'utiliser ! + +```jsx +import React from "react"; + +import { Button } from "./Button"; + +export function App() { + return ( +
+

Mon premier composant React

+
+ ); +} +``` + +On importe notre composant `Button` _(ligne 3)_ et on l'utilise dans notre composant `App` _(ligne 9)_. + +Rien de bien sorcier, n'est-ce pas ? 😊 + +Mais maintenant, on va vouloir donner un peu plus de vie à notre bouton... +Parce que là pour le coup, il est inutile. 😅 + +## 📦 Props + +Les **props** _(pour properties)_ sont des arguments que l'on peut passer à un composant React. +Il s'agit en quelque sorte des paramètres d'une fonction. + +Imaginons ici que l'on veut rajouter : + +- Un texte au bouton +- Une action à effectuer lorsqu'on clique dessus + +Avant de t'expliquer comment faire, je vais te montrer ce que ça donne pour son utilisation : + +```jsx +import { Button } from "./Button"; +import React from "react"; + +export function App() { + function handleClick() { + console.log("Je suis cliqué !"); + } + + return ( +
+

Mon premier composant React

+ +
+ ); +} +``` + +Voyons un peu ce qui change ! + +- Fonction `handleClick` qui affiche un message dans la console _(ligne 6)_ +- On passe la fonction `handleClick` en tant que prop onClick à notre composant `Button` _(ligne 11)_ +- On passe le texte `Clique-moi !` entre la balise ouvrante et fermante de notre composant `Button` _(ligne 12)_ + +Mais alors, comment on fait pour récupérer ces props dans notre composant Button ? + +{% tabs defaultSelectedTab="jsx" %} + +{% tab value="jsx" label="JSX" %} + +```jsx +import React from "react"; + +export function Button(props) { + return ; +} +``` + +{% /tab %} + +{% tab value="tsx" label="TSX" %} + +```tsx +import React from "react"; + +type ButtonProps = { + onClick: () => void; + children: React.ReactNode; +}; + +export function Button(props: ButtonProps) { + return ; +} +``` + +{% /tab %} + +{% /tabs %} + +Comme tu peux voir, on récupère les props passées à notre composant `Button` en tant que paramètre de la fonction. + +Mais tu peux également remarquer qu'il y a une propriété `children` qui n'est pas passée de la même manière que `onClick`. + +C'est totalement normal ! `children` est une prop spéciale qui contient tout ce qui se trouve entre les balises ouvrante et fermante du composant. + +{% callout type="note" title="Le JavaScript inline, c'est pas bien !" %} + +Tu as totalement raison ! On recommande effectivement de ne pas faire du JS inline dans notre HTML et de privilégier un fichier distinct pour notre JavaScript. +Et donc tu sais déjà qu'on va préférer l'utilisation des `addEventListener` 😏 + +... cependant ici, **on ne fait pas du HTML** mais du JSX, et c'est une autre histoire ! + +{% /callout %} + +## 🖇️ Les différentes props + +Il existe plusieurs props que l'on peut passer à un composant React : + +- `children` : Contient tout ce qui se trouve entre les balises ouvrante et fermante du composant +- Les autres props : Tout ce qui est passé en dehors des balises ouvrante et fermante du composant + +Alors évidemment, cette explication est très réductrice, mais on va s'en contenter pour le moment. + +Mais tu te doutes bien qu'il existe des props spécifiques à certains éléments HTML qui peuvent être passées à un composant React. + +## 🔒 Mots clés protégés + +En HTML, il existe des attributs qui sont utilisés dans certains éléments HTML. Cependant, comme ici on ne fait pas du HTML mais du JSX, on ne va pas pouvoir les utiliser tels quels. + +Pour te donner un exemple concret, si tu veux ajouter une classe CSS à un élément, tu ne pourras pas utiliser l'attribut `class` mais `className`. + +Mais alors, pourquoi ? + +Comme le JSX reste avant tout du JavaScript, on ne peut pas utiliser des mots-clés réservés comme `class`, `for`, `default`, etc. + +{% callout type="note" collapsible=true title="Plus d'informations sur les mots-clés protégés" %} + +React comprendra ces mots clés au sein des composants, cependant il va générer un avertissement dans la console du navigateur pour te prévenir que tu utilises un mot-clé protégé qui est "ambigu". + +Cependant, certaines bibliothèques qui utilisent le JSX, comme SolidJS par exemple, utilisent ces mots-clés protégés au sein des composants. Ca ne veut pas dire que c'est "bien" ou "pas bien", mais qu'il faut être conscient de ce que l'on fait et de comment est interprété notre code par la bibliothèque 😉 + +{% /callout %} + +On va donc utiliser des noms d'attributs qui sont similaires à ceux du HTML, mais qui sont adaptés pour le JSX. + +- `class` devient `className` _(pour les classes CSS)_ +- `for` devient `htmlFor` _(pour les labels)_ +- `default` devient `defaultValue` _(pour les champs de formulaire)_ + +Et il en existe bien d'autres, mais on va s'arrêter là pour le moment ! 😊 + +## 👂 Écouteurs d'événements + +Il existe également des attributs spécifiques pour les écouteurs d'événements. +Tu as notamment pu déjà faire la rencontre de `onClick`, mais il en existe bien d'autres ! + +Il s'agit de la syntaxe à adopter avec React pour ajouter des écouteurs d'événements à des composants JSX, et non pas d'utiliser `addEventListener` directement dans le JavaScript 😉 + +J'aimerai beaucoup pouvoir te donner une liste d'exemples d'écouteurs d'événements, mais il y en a tellement que je ne pourrais pas tous les citer ici. +La forme étant toujours la même, tu peux facilement savoir comment les écrire ! + +La forme est simple : + +- Préfixe : `on` +- Événement : nom de l'événement en camelCase + +Par exemple : + +- `click` => `onClick` +- `change` => `onChange` +- `submit` => `onSubmit` +- `mouseenter` => `onMouseEnter` +- `mouseleave` => `onMouseLeave` + +Et si tu cherches une liste complète des écouteurs d'événements, je t'invite à tout simplement consulter le [MDN Web Docs](https://developer.mozilla.org/fr/docs/Web/Events) qui est une mine d'or pour tout ce qui est lié au développement web ! + +Au début, on peut trouver ça un peu déroutant puisqu'on va naturellement les associer à du HTML, mais il faut se rappeler qu'on est dans du JSX _(oui, j'insiste beaucoup là-dessus !)_. + +## Conclusion + +Et voilà, on a créé notre premier composant React ! +On a même rapidement vu comment passer des props à un composant et comment les récupérer. + +Et si pour le prochain article, on parle un peu des states pour rajouter un peu d'intéractivité à nos composants ? +Parce que c'est déjà cool ce qu'on a pu faire, mais on peut faire tellement plus avec React ! 🚀 diff --git a/app/data/docs/react/state-et-cycle-de-vie/page.md b/app/data/docs/react/state-et-cycle-de-vie/page.md new file mode 100644 index 0000000..5933410 --- /dev/null +++ b/app/data/docs/react/state-et-cycle-de-vie/page.md @@ -0,0 +1,141 @@ +--- +title: Le state et le cycle de vie d'un composant React +description: Voyons ensemble comment gérer le state et le cycle de vie d'un composant React ! +tags: [Frontend, React, JavaScript, TypeScript, Bibliothèque, Interface utilisateur (UI)] +--- + +Dans le précédent article, nous avons vu comment créer notre premier composant React avec notamment le concept de **props**. + +Voyons maintenant comment gérer le **state** et le **cycle de vie** d'un composant React ! +Commençons tranquillement avec le **cycle de vie** d'un composant, puisqu'il est indispensable pour comprendre le **state**. + +## 🔄 Cycle de vie + +Le **cycle de vie** d'un composant React est une série d'étapes que traverse un composant, de sa création _(montage)_ à sa destruction _(démontage)_. + +Voici les trois différentes étapes du cycle de vie d'un composant React : + +- **Montage** _(Mounting)_ : le composant est créé et inséré dans le DOM +- **Mise à jour** _(Updating)_ : le composant est mis à jour suite à un changement de props ou de state +- **Démontage** _(Unmounting)_ : le composant est retiré du DOM + +On verra un peu plus en détail ces étapes dans l'article suivant qui traitera un certain hook de React : `useEffect`. + +{% callout type="question" title="Hook, comme le capitaine ? 🦜🏴‍☠️" %} + +Haha, non ! + +Un hook en React, est une fonction qui permet d'exploiter les fonctionnalités de React dans un composant fonctionnel _(fonction)_. + +Bon... c'est un peu du charabia, mais on verra ça plus en détail dans le prochain article car il y a beaucoup à dire sur les hooks ! + +{% /callout %} + +Mais pour le moment, restons en à une vue d'ensemble du cycle de vie ! + +## 🧠 State + +Le state est un objet qui contient les données internes d'un composant. +Il est propre à chaque composant et peut être modifié par ce dernier (à ne pas confondre avec les props qui elles sont immuables). + +Mais alors, pourquoi utiliser un state alors qu'on pourrait tout simplement déclarer une variable dans notre composant ? + +Prenons cet exemple : + +```jsx showLineNumbers +import React from "react"; + +export function Counter() { + let count = 0; + + function increment() { + count += 1; + console.log("Increment", count); + } + + return ; +} +``` + +[Voir l'exemple sur PlayCode](https://playcode.io/1940876) + +On serait tentés de croire que ce code fonctionne. Après tout, en vanilla JS _(JavaScript pur)_, on pourrait tout à fait faire ça ! +Et maintenant tu t'en doutes _(sinon pourquoi j'en parlerai ?)_, ce code ne fonctionne pas. + +Pourtant, si on regarde la console du navigateur on voit bien que la variable `count` est bien incrémentée ! + +La raison est très simple : React ne sait pas que la variable `count` a été modifiée. +Pour être plus précis, React ne sait pas qu'il doit mettre à jour l'interface utilisateur _(UI)_ suite à la modification de count. + +C'est là qu'intervient le **state** ! +Le **state** est **réactif** et permet à React de savoir quand il doit mettre à jour l'interface utilisateur _(UI)_. + +## 📝 Déclaration du state + +Pour déclarer un **state**, on utilise le **hook** `useState` de React. + +```jsx +const [count, setCount] = React.useState(0); +``` + +Et là tu vas peut-être te demander une chose... + +{% callout type="question" title="Ouh là... Pourquoi on a deux assignements ?" %} + +Bien vu ! Effectivement on va avoir deux assignements pour déclarer un state : + +- `count` : la valeur du state +- `setCount` : la fonction qui permet de modifier la valeur du state + +Si tu as déjà fait de la POO, le principe de **getter** et **setter** te sera familier puisque c'est un peu le même principe ! + +Le hook `useState` prend en paramètre la **valeur initiale du state** _(ici 0)_ et retourne un tableau avec la valeur du state et la fonction pour le modifier. + +{% /callout %} + +## 🔄 Utilisation du state + +Maintenant que notre state est déclaré, on peut l'utiliser dans notre composant. + +```jsx showLineNumbers +import React from "react"; + +export function Counter() { + const [count, setCount] = React.useState(0); + + function increment() { + setCount(count + 1); + } + + return ; +} +``` + +[Voir l'exemple sur PlayCode](https://playcode.io/1940705) + +Et voilà ! Pas besoin de plus pour gérer un state en React 😉 + +Mais qu'est-ce qu'il se passe sous le capot ? +C'est un peu plus complexe que ça, mais pour faire simple : + +### ⚙️ Montage du composant _(Mounting)_ + +On vient prévenir React que notre composant va avoir un **state** et on lui donne une valeur initiale _(ici 0)_. + +### 🔧 Mise à jour du composant (Updating) + +Ce state, à chaque modification, va déclencher un nouveau rendu du composant. + +### 🗑️ Démontage du composant (Unmounting) + +Et enfin, quand le composant est retiré du DOM, le state est détruit avec lui. + +Ce fonctionnement est identique pour les props donnés à un composant d'ailleurs ! +React est vraiment bien fait pour ça 😊 + +## Conclusion + +Plutôt simple, non ? +Alors maintenant que tu sais comment gérer le **state** et le **cycle de vie** d'un composant React, tu es prêt à te pencher sur la prochaine étape _(et pas des moindres)_ : les **hooks** ! + +Mais pour l'heure, je te laisse jouer avec les **states** et les **props** pour bien comprendre comment tout ça fonctionne. diff --git a/app/lib/navigation.ts b/app/lib/navigation.ts index a897dc8..5b38f5c 100644 --- a/app/lib/navigation.ts +++ b/app/lib/navigation.ts @@ -4,6 +4,10 @@ export const navigation = [ links: [ { title: "Introduction", href: "/docs/react" }, { title: "Initialisation", href: "/docs/react/initialisation" }, + { title: "Syntaxe JSX", href: "/docs/react/jsx" }, + { title: "Premier composant", href: "/docs/react/premier-composant" }, + { title: "State et cycle de vie", href: "/docs/react/state-et-cycle-de-vie" }, + { title: "Hooks", href: "/docs/react/hooks" }, ], }, { diff --git a/app/markdoc/tags.tsx b/app/markdoc/tags.tsx index 1f08a4b..3ef1ae3 100644 --- a/app/markdoc/tags.tsx +++ b/app/markdoc/tags.tsx @@ -1,6 +1,7 @@ import { QuickLink, QuickLinks } from "@syntax/QuickLinks"; import { TabContent, Tabs } from "@/components/md/Tabs"; import { Callout } from "@syntax/Callout"; +import React from "react"; const tags = { callout: { @@ -12,8 +13,20 @@ const tags = { matches: ["note", "warning", "question"], errorLevel: "critical", }, + collapsible: { + type: Boolean, + default: false, + }, + }, + render: (props: { + title: string; + type?: "note" | "warning" | "question"; + collapsible?: boolean; + children: React.ReactNode; + }) => { + console.log(props); + return ; }, - render: Callout, }, figure: { selfClosing: true,