rework/lightweight #12

Merged
GauthierWebDev merged 106 commits from rework/lightweight into main 2025-04-21 16:27:38 +00:00
4 changed files with 267 additions and 220 deletions
Showing only changes of commit 15cc58f07f - Show all commits

View File

@ -32,7 +32,7 @@ export function QuestionIcon(props: GradientProps) {
stroke-linejoin="round" stroke-linejoin="round"
/> />
<path <path
d="m 16.39 14.617 l 1.179 -3.999 C 17.38 9.304 16.133 9.127 15.469 10.645 C 15.306 11.269 14.71 11.12 14.71 10.537 a 1.66 1.66 5 1 1 3.808 0.217 l -1.5182 5.4314 a 0.602 0.602 5 0 1 -1.1795 -0.1032 Z" d="M 15.613 15.7139 C 15.8705 15.1461 17.758 11.3442 17.8109 10.4986 C 17.9128 8.8632 15.5223 8.1803 14.6161 9.3819 C 14.4617 9.5862 13.8186 9.7889 13.8694 9.2081 C 14.0808 7.755 16.4352 7.3025 17.9493 8.5954 C 18.5705 9.1256 18.8798 9.9497 18.7641 10.6412 C 18.5471 11.9402 16.7786 15.9192 16.7786 15.9192 C 16.2095 17.065 15.3394 16.3173 15.613 15.7139 Z"
class="fill-[var(--icon-foreground)] stroke-[color:var(--icon-foreground)]" class="fill-[var(--icon-foreground)] stroke-[color:var(--icon-foreground)]"
stroke-width={2} stroke-width={2}
stroke-linecap="round" stroke-linecap="round"

View File

@ -4,6 +4,9 @@ description: Découvre les hooks de React, une fonctionnalité qui te permet de
tags: [Frontend, React, JavaScript, TypeScript, Bibliothèque, Interface utilisateur (UI)] tags: [Frontend, React, JavaScript, TypeScript, Bibliothèque, Interface utilisateur (UI)]
--- ---
import Callout from "@/components/Callout";
import tabs from "./tabs";
Ça y est, on rentre dans le vif du sujet avec les **hooks** de React ! Ç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. 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.
@ -21,75 +24,7 @@ C'était pas mal, mais ça devenait vite compliqué à gérer, notamment pour pa
Pour te donner un aperçu, voici à quoi ressemblait un composant de classe avec les trois étapes du cycle de vie : Pour te donner un aperçu, voici à quoi ressemblait un composant de classe avec les trois étapes du cycle de vie :
{% tabs defaultSelectedTab="jsx" %} <tabs.reactClassComponent />
{% 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 <div>{this.state.count}</div>;
}
}
```
{% /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 <div>{this.state.count}</div>;
}
}
```
{% /tab %}
{% /tabs %}
Comme dirait l'un de mes chers confrères jury : Comme dirait l'un de mes chers confrères jury :
@ -126,39 +61,7 @@ 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 😇 Alors sur le papier c'est top, mais maintenant je te laisse t'amuser à comprendre comment ça fonctionne 😇
{% tabs defaultSelectedTab="1" %} <tabs.reactUseEffectSyntaxes />
{% 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 ? 😂 Pas cool, hein ? 😂
Et bien dans ces exemples, on a trois manières d'écrire un useEffect : Et bien dans ces exemples, on a trois manières d'écrire un useEffect :
@ -167,11 +70,9 @@ Et bien dans ces exemples, on a trois manières d'écrire un useEffect :
2. Le hook est exécuté à chaque mise à jour 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é 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" %} <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.
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>
{% /callout %}
Mais alors, comment on fait pour gérer ces étapes avec des composants fonctionnels ? 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. 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.
@ -183,31 +84,19 @@ Selon ce tableau, le hook sera exécuté à des moments différents du cycle de
### ⚙️ ComponentDidMount ### ⚙️ ComponentDidMount
```jsx <tabs.reactUseEffectMount />
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. 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. On peut donc dire que c'est l'équivalent de `componentDidMount` pour les composants de classe.
### 🔧 ComponentDidUpdate ### 🔧 ComponentDidUpdate
```jsx <tabs.reactUseEffectUpdate />
React.useEffect(() => {
// ...
});
```
Ici, le tableau de dépendances est absent _(et tout va bien, il est optionnel !)_. 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. Le hook sera exécuté à chaque mise à jour du composant, ainsi que lors du premier rendu.
```jsx <tabs.reactUseEffectUpdateDependency />
React.useEffect(() => {
// ...
}, [props.uneProp]);
```
Dans ce cas, le tableau de dépendances contient la propriété `uneProp` de `props`. 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é. Le hook sera exécuté à chaque mise à jour du composant _(ainsi qu'au montage)_, mais seulement si la propriété `uneProp` a changé.
@ -217,14 +106,7 @@ Le hook sera exécuté à chaque mise à jour du composant _(ainsi qu'au montage
Et là, tu te dis : "Mais comment je fais pour gérer le démontage du composant ?". 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 😏 Hehehe, c'est là que ça devient intéressant 😏
```jsx <tabs.reactUseEffectUnmount />
React.useEffect(() => {
// ...
return () => {
// ...
};
}, []);
```
Tu as vu ce petit `return` ? Et bien, c'est notre équivalent de `componentWillUnmount` pour les composants de classe ! Tu as vu ce petit `return` ? Et bien, c'est notre équivalent de `componentWillUnmount` pour les composants de classe !
@ -242,65 +124,9 @@ Pour éviter que React se dise "Tiens, il y a eu un changement, je vais re-rendr
Allez, mettons un peu ce qu'on voit de voir en pratique ! Allez, mettons un peu ce qu'on voit de voir en pratique !
{% tabs defaultSelectedTab="jsx" %} Voici un exemple de code qui utilise `useEffect` pour gérer le cycle de vie d'un composant :
{% tab value="jsx" label="JSX" %} <tabs.reactUseEffectExample />
```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 <button onClick={increment}>{count}</button>;
};
```
{% /tab %}
{% tab value="tsx" label="TSX" %}
```tsx
import React from "react";
export const Counter = () => {
const [count, setCount] = React.useState<number>(0);
React.useEffect(() => {
console.log("Component mounted");
return () => {
console.log("Component unmounted");
};
}, []);
React.useEffect(() => {
console.log("Component updated");
});
const increment = () => setCount(count + 1);
return <button onClick={increment}>{count}</button>;
};
```
{% /tab %}
{% /tabs %}
### 🔢 On revient sur le cycle de vie ! ### 🔢 On revient sur le cycle de vie !
@ -309,39 +135,16 @@ On revient sur le cycle de vie d'un composant maintenant qu'on a vu `useEffect`
Je vais te donner un exemple de code supplémentaire et tu vas devoir deviner l'ordre d'apparition des messages dans la console. 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 <tabs.reactUseEffectChallenge />
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 <div>{logInRender()}</div>;
};
```
Voici les possibilités : Voici les possibilités :
{% callout type="question" title="Quel est l'ordre d'apparition des messages dans la console ?" %} <Callout type="question" title="Quel est l'ordre d'apparition des messages dans la console ?">
- **A** - 4, 2, 1, 3
- **A** - 4, 2, 1, 3 - **B** - 2, 4, 1, 3
- **B** - 2, 4, 1, 3 - **C** - 1, 2, 3, 4
- **C** - 1, 2, 3, 4 - **D** - La réponse D
- **D** - La réponse D </Callout>
{% /callout %}
## 🧩 Les autres hooks ## 🧩 Les autres hooks

View File

@ -0,0 +1,244 @@
import { Snippet } from "@/components/Snippet";
const reactClassComponentSnippets = [
{
name: "JSX",
codeLanguage: "jsx",
withLineNumbers: true,
code: `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 <div>{this.state.count}</div>;
}
}`,
},
{
name: "TSX",
codeLanguage: "tsx",
withLineNumbers: true,
code: `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 <div>{this.state.count}</div>;
}
}`,
},
];
const reactUseEffectSyntaxesSnippets = [
{
name: "Syntaxe 1",
codeLanguage: "jsx",
code: `useEffect(() => {
// Code à exécuter
}, []);`,
},
{
name: "Syntaxe 2",
codeLanguage: "jsx",
code: `useEffect(() => {
// Code à exécuter
}, [props.uneProp]);`,
},
{
name: "Syntaxe 3",
codeLanguage: "jsx",
code: `useEffect(() => {
// Code à exécuter
});`,
},
];
const reactUseEffectMountSnippets = [
{
name: "Exemple de `useEffect` pour le montage",
codeLanguage: "jsx",
code: `useEffect(() => {
// Code à exécuter lors du montage
// (équivalent à componentDidMount)
}, []);`,
},
];
const reactUseEffectUpdateSnippets = [
{
name: "Exemple de `useEffect` pour la mise à jour",
codeLanguage: "jsx",
code: `useEffect(() => {
// Code à exécuter lors du montage et de la mise à jour
// (équivalent à componentDidMount et componentDidUpdate)
});`,
},
];
const reactUseEffectUpdateDependencySnippets = [
{
name: "Exemple de `useEffect` pour la mise à jour",
codeLanguage: "jsx",
code: `useEffect(() => {
// Code à exécuter lors du montage et de la mise à jour
// (équivalent à componentDidMount et componentDidUpdate)
}, [props.uneProp]);`,
},
];
const reactUseEffectUnmountSnippets = [
{
name: "Exemple de `useEffect` pour le démontage",
codeLanguage: "jsx",
code: `useEffect(() => {
// Code à exécuter lors du montage
return () => {
// Code à exécuter lors du démontage
// (équivalent à componentWillUnmount)
};
}, []);`,
},
];
const reactUseEffectExampleSnippets = [
{
name: "JSX",
codeLanguage: "jsx",
withLineNumbers: true,
code: `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 <button onClick={increment}>{count}</button>;
};`,
},
{
name: "TSX",
codeLanguage: "tsx",
withLineNumbers: true,
code: `import React from "react";
export const Counter = () => {
const [count, setCount] = React.useState<number>(0);
React.useEffect(() => {
console.log("Component mounted");
return () => {
console.log("Component unmounted");
};
}, []);
React.useEffect(() => {
console.log("Component updated");
});
const increment = () => setCount(count + 1);
return <button onClick={increment}>{count}</button>;
};`,
},
];
const reactUseEffectChallengeSnippets = [
{
name: "Testons le cycle de vie d'un composant",
codeLanguage: "jsx",
withLineNumbers: true,
code: `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 <div>{logInRender()}</div>;
};`,
},
];
export default {
reactClassComponent: () => <Snippet snippets={reactClassComponentSnippets} />,
reactUseEffectSyntaxes: () => (
<Snippet snippets={reactUseEffectSyntaxesSnippets} />
),
reactUseEffectMount: () => <Snippet snippets={reactUseEffectMountSnippets} />,
reactUseEffectUpdate: () => (
<Snippet snippets={reactUseEffectUpdateSnippets} />
),
reactUseEffectUpdateDependency: () => (
<Snippet snippets={reactUseEffectUpdateDependencySnippets} />
),
reactUseEffectUnmount: () => (
<Snippet snippets={reactUseEffectUnmountSnippets} />
),
reactUseEffectExample: () => (
<Snippet snippets={reactUseEffectExampleSnippets} />
),
reactUseEffectChallenge: () => (
<Snippet snippets={reactUseEffectChallengeSnippets} />
),
};

View File

@ -90,7 +90,7 @@ Et voilà ! Pas besoin de plus pour gérer un state en React 😉
Mais qu'est-ce qu'il se passe sous le capot ? Mais qu'est-ce qu'il se passe sous le capot ?
C'est un peu plus complexe que ça, mais pour faire simple : C'est un peu plus complexe que ça, mais pour faire simple :
### ⚙️ Montage du composant ### ⚙️ 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)_. On vient prévenir React que notre composant va avoir un **state** et on lui donne une valeur initiale _(ici 0)_.