diff --git a/app/icons/QuestionIcon.tsx b/app/icons/QuestionIcon.tsx index 303f9bc..1c03d60 100644 --- a/app/icons/QuestionIcon.tsx +++ b/app/icons/QuestionIcon.tsx @@ -32,7 +32,7 @@ export function QuestionIcon(props: GradientProps) { stroke-linejoin="round" /> {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 : @@ -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 😇 -{% 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 : @@ -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 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 %} + + 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. + 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. @@ -183,31 +84,19 @@ Selon ce tableau, le hook sera exécuté à des moments différents du cycle de ### ⚙️ 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é. @@ -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 ?". 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 ! @@ -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 ! -{% 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" %} - -```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 ! @@ -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. -```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 %} + + - **A** - 4, 2, 1, 3 + - **B** - 2, 4, 1, 3 + - **C** - 1, 2, 3, 4 + - **D** - La réponse D + ## 🧩 Les autres hooks diff --git a/app/pages/docs/react/hooks/tabs.tsx b/app/pages/docs/react/hooks/tabs.tsx new file mode 100644 index 0000000..f91a6fe --- /dev/null +++ b/app/pages/docs/react/hooks/tabs.tsx @@ -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
{this.state.count}
; + } +}`, + }, + { + 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
{this.state.count}
; + } +}`, + }, +]; + +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 ; +};`, + }, + { + name: "TSX", + codeLanguage: "tsx", + 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 ; +};`, + }, +]; + +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
{logInRender()}
; +};`, + }, +]; + +export default { + reactClassComponent: () => , + reactUseEffectSyntaxes: () => ( + + ), + reactUseEffectMount: () => , + reactUseEffectUpdate: () => ( + + ), + reactUseEffectUpdateDependency: () => ( + + ), + reactUseEffectUnmount: () => ( + + ), + reactUseEffectExample: () => ( + + ), + reactUseEffectChallenge: () => ( + + ), +}; diff --git a/app/pages/docs/react/state-et-cycle-de-vie/+Page.mdx b/app/pages/docs/react/state-et-cycle-de-vie/+Page.mdx index 4e351ad..41a36e9 100644 --- a/app/pages/docs/react/state-et-cycle-de-vie/+Page.mdx +++ b/app/pages/docs/react/state-et-cycle-de-vie/+Page.mdx @@ -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 ? 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)_.