Cours Next.js · Module 1: Configuration de Next.js
Système de composants - composants client et serveur
Dans cette leçon10
À Quantum Metropolis, les architectes des systèmes quantiques doivent toujours prendre des décisions fondamentales : quels processus exécuter sur les processeurs quantiques centraux (pour les tâches nécessitant la plus grande puissance de calcul) et lesquels exécuter sur les appareils des citoyens (pour une interactivité instantanée). Cette dichotomie reflète parfaitement le changement révolutionnaire que l'App Router a introduit dans Next.js 13 et que Next.js 16 prolonge - la division entre Server Components et Client Components.
La révolution des React Server Components
Les React Server Components (RSC) représentent le plus grand changement de paradigme dans React depuis l'introduction des Hooks. Avant leur apparition, tous les composants React étaient rendus côté serveur ou côté client comme un tout cohérent. Désormais, nous pouvons créer des applications où certains composants sont rendus exclusivement sur le serveur, d'autres sur le client, et où les données et les structures d'interface circulent harmonieusement entre eux.
C'est comme la différence entre les systèmes de transport traditionnels de Quantum Metropolis, où les véhicules se déplacent uniquement le long de routes précises, et le nouveau système de transport quantique, où la matière peut être téléportée directement à destination.
Server Components - les bases
Dans l'App Router de Next.js 16, tous les composants sont des Server Components par défaut, à moins que nous ne les marquions explicitement comme Client Components.
Les Server Components sont rendus sur le serveur et n'envoient aucun JavaScript au navigateur. C'est comme les processeurs de calcul quantique de Quantum Metropolis, qui effectuent des calculs complexes et ne transmettent que les résultats finaux aux appareils des citoyens.
Avantages des Server Components :
- Accès aux ressources serveur - accès direct aux bases de données, au système de fichiers et à d'autres ressources serveur
- Sécurité - les données et la logique sensibles restent sur le serveur
- Découpage automatique du code - seul le code nécessaire est envoyé au client
- Taille de bundle JavaScript plus petite - pas besoin d'envoyer le code de rendu
- Meilleur SEO - rendu statique plus rapide
- Meilleures performances au premier chargement - moins de JavaScript à télécharger et à exécuter
Exemple de Server Component :
1// app/dashboard/stats/page.tsx - Server Component (by default)
2export default async function StatsPage() {
3 // This code runs ONLY on the server
4 const stats = await fetchQuantumSystemStats();
5
6 return (
7 <section className="quantum-stats">
8 <h1>Quantum Systems Statistics</h1>
9 <div className="stats-grid">
10 {stats.map(stat => (
11 <StatCard key={stat.id} name={stat.name} value={stat.value} />
12 ))}
13 </div>
14 </section>
15 );
16}Remarque plusieurs aspects importants :
- L'utilisation directe de async/await dans le composant - c'est possible uniquement dans les Server Components
- Pas d'utilisation des hooks React (useState, useEffect, etc.) - ils ne sont pas disponibles dans les Server Components
- Pas de gestion d'événements - les Server Components ne peuvent pas gérer les événements utilisateur
Client Components - les bases
Les Client Components apportent l'interactivité : à la première visite, Next.js les pré-rend en HTML sur le serveur, puis ils sont hydratés dans le navigateur et continuent de fonctionner côté client. Pour marquer un composant comme Client Component, nous ajoutons la directive "use client" en haut du fichier.
Ce processus s'appelle l'hydratation : dans le navigateur, React attache les gestionnaires d'événements au HTML rendu plus tôt sur le serveur. Ce n'est qu'après l'hydratation que les boutons réagissent aux clics, et les effets de useEffect s'exécutent après elle.
C'est comme les appareils quantiques personnels des citoyens de Quantum Metropolis - plus légers, mais réagissant instantanément aux actions de l'utilisateur.
Avantages des Client Components :
- Interactivité - gestion des événements utilisateur (clics, saisie de texte, etc.)
- Hooks React - possibilité d'utiliser useState, useEffect, useContext et d'autres hooks
- Accès aux API du navigateur - window, document, localStorage, etc.
- Effets côté client - animations, interactions en temps réel
Exemple de Client Component :
1// app/dashboard/interactive-controls.tsx
2"use client"; // This directive marks the client component
3
4import { useState, useEffect } from 'react';
5
6export default function InteractiveControls() {
7 // This code runs in the user's browser
8 const [activeSystem, setActiveSystem] = useState('energy');
9 const [status, setStatus] = useState('idle');
10
11 useEffect(() => {
12 // Effects run only on the client
13 const timer = setTimeout(() => {
14 setStatus('ready');
15 }, 1000);
16
17 return () => clearTimeout(timer);
18 }, []);
19
20 return (
21 <div className="control-panel">
22 <h2>System: {activeSystem}</h2>
23 <p>Status: {status}</p>
24 <div className="button-group">
25 <button onClick={() => setActiveSystem('transport')}>
26 Transport
27 </button>
28 <button onClick={() => setActiveSystem('energy')}>
29 Energy
30 </button>
31 <button onClick={() => setActiveSystem('security')}>
32 Security
33 </button>
34 </div>
35 </div>
36 );
37}Aspects clés :
- La directive
"use client"au début du fichier - L'utilisation des hooks React (useState, useEffect)
- La gestion des événements (onClick)
Combiner Server et Client Components
La fonctionnalité la plus puissante du nouveau modèle est la possibilité de combiner Server et Client Components dans une seule application. Les Server Components peuvent rendre des Client Components, mais pas l'inverse (avec quelques exceptions utilisant des patterns). C'est comme le système de transport hybride de Quantum Metropolis, où les routes principales sont assurées par la téléportation quantique, et le dernier kilomètre par des véhicules personnels.
Exemple de combinaison de composants :
1// app/dashboard/page.tsx - Server Component
2export default async function DashboardPage() {
3 // This code runs ONLY on the server
4 const dashboardData = await fetchDashboardData();
5
6 return (
7 <main className="quantum-dashboard">
8 <h1>Quantum Metropolis Control Panel</h1>
9
10 {/* Server Component */}
11 <SystemStatusPanel systemsData={dashboardData.systems} />
12
13 {/* Client Component (imported and used in Server Component) */}
14 <InteractiveControls />
15 </main>
16 );
17}1// app/dashboard/system-status-panel.tsx - Server Component (by default)
2export default function SystemStatusPanel({ systemsData }) {
3 return (
4 <div className="system-status-panel">
5 <h2>System Status</h2>
6 <div className="status-grid">
7 {systemsData.map(system => (
8 <SystemStatusCard key={system.id} system={system} />
9 ))}
10 </div>
11 </div>
12 );
13}1// app/dashboard/system-status-card.tsx - Server Component (by default)
2import { StatusIndicator } from './status-indicator';
3
4export default function SystemStatusCard({ system }) {
5 return (
6 <div className="status-card">
7 <h3>{system.name}</h3>
8 <p>Performance: {system.performance}%</p>
9
10 {/* Client Component used in Server Component */}
11 <StatusIndicator status={system.status} />
12 </div>
13 );
14}1// app/dashboard/status-indicator.tsx - Client Component
2"use client";
3
4import { useState, useEffect } from 'react';
5
6export function StatusIndicator({ status }) {
7 const [blinking, setBlinking] = useState(false);
8
9 useEffect(() => {
10 const interval = setInterval(() => {
11 setBlinking(prev => !prev);
12 }, 500);
13
14 return () => clearInterval(interval);
15 }, []);
16
17 return (
18 <div className={`status-indicator ${status} ${blinking ? 'blink' : ''}`}>
19 {status === 'active' ? 'Active' : 'Inactive'}
20 </div>
21 );
22}Stratégies d'optimisation lors de la conception des composants
Décider correctement quels composants doivent être des Server Components et lesquels doivent être des Client Components est crucial pour les performances de l'application. Voici quelques stratégies pour t'aider à prendre ces décisions :
1. Le Server Component comme choix par défaut
Commence toujours par des Server Components, sauf si tu as besoin de fonctionnalités disponibles uniquement dans les Client Components. C'est similaire à l'approche de Quantum Metropolis, où les unités de calcul centrales sont utilisées par défaut, sauf si une interactivité instantanée est nécessaire.
2. Interactivité granulaire
Au lieu de marquer un grand composant entier comme « use client », extrait uniquement les parties interactives dans des Client Components séparés, en laissant le reste en Server Components.
Avant optimisation :
1// LESS OPTIMAL - entire component is a Client Component
2"use client";
3import { useState } from 'react';
4
5export default function ControlPanel() {
6 const [activeTab, setActiveTab] = useState('overview');
7
8 return (
9 <div className="control-panel">
10 <h2>Control Panel</h2>
11 <p>This panel allows you to manage quantum systems.</p>
12
13 <div className="tabs">
14 <button onClick={() => setActiveTab('overview')}>Overview</button>
15 <button onClick={() => setActiveTab('settings')}>Settings</button>
16 </div>
17
18 <div className="tab-content">
19 {activeTab === 'overview' ? (
20 <OverviewContent /> // Lots of static content
21 ) : (
22 <SettingsContent /> // Lots of static content
23 )}
24 </div>
25 </div>
26 );
27}Après optimisation :
1// BETTER - split into Client and Server Components
2
3// control-panel.tsx - Server Component
4import { TabController } from './tab-controller';
5import { OverviewContent } from './overview-content';
6import { SettingsContent } from './settings-content';
7
8export default function ControlPanel() {
9 return (
10 <div className="control-panel">
11 <h2>Control Panel</h2>
12 <p>This panel allows you to manage quantum systems.</p>
13
14 {/* Only TabController is a Client Component; the tab contents stay on the server */}
15 <TabController overview={<OverviewContent />} settings={<SettingsContent />} />
16 </div>
17 );
18}
19
20// tab-controller.tsx - Client Component
21"use client";
22import { useState, type ReactNode } from 'react';
23
24export function TabController({ overview, settings }: { overview: ReactNode; settings: ReactNode }) {
25 const [activeTab, setActiveTab] = useState('overview');
26
27 return (
28 <>
29 <div className="tabs">
30 <button onClick={() => setActiveTab('overview')}>Overview</button>
31 <button onClick={() => setActiveTab('settings')}>Settings</button>
32 </div>
33
34 <div className="tab-content">
35 {activeTab === 'overview' ? overview : settings}
36 </div>
37 </>
38 );
39}Un composant importé dans un fichier avec "use client" devient lui aussi un Client Component. C'est pourquoi le Server Component ControlPanel importe OverviewContent et SettingsContent et les passe à TabController comme props, tout comme children.
3. Le pattern « Container et Présentation »
Utilise les Server Components comme « containers » pour la récupération de données et l'organisation, et les Client Components comme « composants de présentation » responsables de l'interactivité.
1// products-container.tsx - Server Component (container)
2export default async function ProductsContainer() {
3 const products = await fetchProducts();
4
5 return <ProductsList products={products} />;
6}
7
8// products-list.tsx - Client Component (presentation)
9"use client";
10import { useState } from 'react';
11
12export function ProductsList({ products }) {
13 const [sortBy, setSortBy] = useState('name');
14 const sortedProducts = [...products].sort((a, b) =>
15 sortBy === 'price' ? a.price - b.price : a.name.localeCompare(b.name)
16 );
17
18 return (
19 <div>
20 <div className="sorting">
21 <button onClick={() => setSortBy('name')}>Sort by name</button>
22 <button onClick={() => setSortBy('price')}>Sort by price</button>
23 </div>
24
25 <ul className="products">
26 {sortedProducts.map(product => (
27 <li key={product.id}>{product.name} - {product.price}</li>
28 ))}
29 </ul>
30 </div>
31 );
32}Flux de données entre Server et Client Components
Passer des données entre le serveur et le client nécessite une approche particulière. Dans le modèle RSC, les props descendent dans l'arbre des composants du serveur vers le client ; dans l'autre sens, le client envoie des données en appelant des Server Actions (tu les découvriras dans un instant). C'est comme les canaux quantiques de Quantum Metropolis - les ordres descendent du hub central vers les appareils personnels, et les rapports remontent par un canal séparé.
Passer des données aux Client Components :
1// server-component.tsx - Server Component
2async function ServerComponent() {
3 const data = await fetchSensitiveData();
4
5 // Pass processed data to client
6 return <ClientComponent processedData={data.publicPart} />;
7}
8
9// client-component.tsx - Client Component
10"use client";
11
12export function ClientComponent({ processedData }) {
13 // Using data passed from the server
14 return <div>{processedData.map(item => <p key={item.id}>{item.name}</p>)}</div>;
15}Gérer les actions utilisateur depuis les Client Components :
Quand un Client Component doit effectuer une action impliquant le serveur, nous utilisons les Server Actions ou les Route Handlers (fichiers route.ts ; dans le Pages Router, leur équivalent s'appelle API Routes).
Utiliser les Server Actions (stables depuis Next.js 14) :
1// actions.js - Server Actions
2"use server";
3
4export async function updateSystemStatus(systemId, newStatus) {
5 // This function runs on the server
6 await database.systems.update(systemId, { status: newStatus });
7 return { success: true };
8}
9
10// client-component.tsx - Client Component
11"use client";
12import { useState } from 'react';
13import { updateSystemStatus } from './actions';
14
15export function StatusToggle({ systemId, initialStatus }) {
16 const [status, setStatus] = useState(initialStatus);
17
18 async function handleToggle() {
19 const newStatus = status === 'active' ? 'inactive' : 'active';
20 const result = await updateSystemStatus(systemId, newStatus);
21
22 if (result.success) {
23 setStatus(newStatus);
24 }
25 }
26
27 return (
28 <button onClick={handleToggle}>
29 {status === 'active' ? 'Deactivate' : 'Activate'}
30 </button>
31 );
32}Différences de rechargement et de navigation
Dans le modèle React traditionnel, le rechargement d'une page ou la navigation signifiait souvent redémarrer toute l'application. Dans le modèle des React Server Components, la navigation est plus optimale :
- Les Server Components sont à nouveau rendus sur le serveur lors de la navigation
- Les Client Components des layouts partagés conservent leur état pendant la navigation ; les composants de la page que tu quittes sont démontés par défaut
- Les transitions de page sont plus fluides grâce au mécanisme de « navigation instantanée »
C'est comme les commutateurs quantiques avancés de Quantum Metropolis - ils permettent des transitions instantanées entre différents layouts sans interrompre le fonctionnement des systèmes.
Erreurs courantes et pièges
1. Utiliser des hooks React dans des Server Components
1// WRONG - Server Component trying to use React hooks
2export default function ServerComponent() {
3 // This will cause an error!
4 const [count, setCount] = useState(0);
5
6 return <div>Count: {count}</div>;
7}2. Tenter d'accéder aux API du navigateur dans des Server Components
1// WRONG - Server Component trying to use browser APIs
2export default function ServerComponent() {
3 // This will cause an error! (ReferenceError: window is not defined)
4 const windowWidth = window.innerWidth;
5
6 return <div>Width: {windowWidth}px</div>;
7}3. Passer des fonctions d'un Server Component à un Client Component
1// WRONG - Server Component passes a regular function to a Client Component
2// app/page.tsx (Server Component)
3import ClientButton from './client-button';
4
5export default function Page() {
6 const handleClick = () => console.log("Clicked!");
7
8 // This won't work! Props passed to a Client Component must be serializable
9 return <ClientButton onClick={handleClick} />;
10}Les Server Actions font exception : tu peux passer une fonction marquée "use server" à un Client Component.
Patterns avancés pour utiliser Server et Client Components
1. Le pattern Provider
Si tu as besoin de partager un état entre différents Client Components, utilise un Client Provider à un niveau élevé. C'est similaire aux unités de synchronisation centrales de Quantum Metropolis, qui coordonnent les actions des différents appareils personnels.
1// providers.tsx - Client Component
2"use client";
3import { createContext, useState } from 'react';
4
5export const ThemeContext = createContext({ theme: 'light', toggleTheme: () => {} });
6
7export function ThemeProvider({ children }) {
8 const [theme, setTheme] = useState('light');
9 const toggleTheme = () => setTheme(prev => prev === 'light' ? 'dark' : 'light');
10
11 return (
12 <ThemeContext.Provider value={{ theme, toggleTheme }}>
13 {children}
14 </ThemeContext.Provider>
15 );
16}1// layout.tsx - Server Component
2import { ThemeProvider } from './providers';
3
4export default function RootLayout({ children }: { children: React.ReactNode }) {
5 return (
6 <html>
7 <body>
8 <ThemeProvider>
9 {children}
10 </ThemeProvider>
11 </body>
12 </html>
13 );
14}2. Le pattern « Lifting State Up »
Quand tu as plusieurs Client Components qui ont besoin d'accéder au même état, fais remonter l'état vers un ancêtre Client Component commun, et non vers un Server Component.
1// parent.tsx - Server Component
2export default function ParentServerComponent() {
3 return <ClientParent />;
4}
5
6// client-parent.tsx - Client Component (common ancestor)
7"use client";
8import { useState } from 'react';
9
10export function ClientParent() {
11 const [sharedState, setSharedState] = useState('initial');
12
13 return (
14 <div>
15 <ClientChildA sharedState={sharedState} setSharedState={setSharedState} />
16 <ClientChildB sharedState={sharedState} />
17 </div>
18 );
19}3. Utilisation stratégique de Suspense
Utilise le composant Suspense pour gérer les états de chargement dans une application hybride :
1// page.tsx - Server Component
2import { Suspense } from 'react';
3
4export default function Page() {
5 return (
6 <div>
7 <h1>Quantum Metropolis System Data</h1>
8
9 <Suspense fallback={<div>Loading data...</div>}>
10 <AsyncDataComponent />
11 </Suspense>
12 </div>
13 );
14}
15
16// async-data-component.tsx - Server Component with asynchronous code
17async function AsyncDataComponent() {
18 // This component can be suspended during loading
19 const data = await fetchData();
20
21 return <div>{/* Rendering data */}</div>;
22}Résumé
Le système de composants de Next.js 16 basé sur les React Server Components est un changement révolutionnaire qui combine les avantages du rendu côté serveur avec l'interactivité côté client. Tout comme à Quantum Metropolis, où la téléportation quantique et les véhicules personnels créent un système de transport hybride, dans Next.js les composants serveur et client travaillent ensemble, créant une interface efficace, évolutive et conviviale.
Lorsque tu conçois des applications Next.js 16 :
- Utilise les Server Components par défaut pour de meilleures performances et plus de sécurité
- Passe aux Client Components uniquement quand tu as besoin d'interactivité, de hooks ou des API du navigateur
- Découpe les composants de façon granulaire pour minimiser la quantité de JavaScript envoyée au client
- Souviens-toi du sens des props - du serveur vers le client (dans l'autre sens, tu envoies des données avec des Server Actions)
- Utilise les Server Actions pour le traitement des formulaires et d'autres actions utilisateur
Une bonne utilisation des Server et Client Components te permettra de créer des applications Next.js de nouvelle génération qui combinent chargement rapide, performance SEO et interfaces utilisateur riches - sans les compromis qui étaient nécessaires dans les applications React traditionnelles.
Dans la prochaine leçon, nous passerons à la pratique et créerons notre première application « Hello World » avec Next.js 16 et l'App Router, où nous appliquerons les connaissances acquises sur les Server et Client Components.
Code de cette leçon : App.tsx
1// Hooks - useState et useEffect - Metropolis Quantum
2import React, { useState, useEffect } from 'react';
3
4console.log("Hooks React - useState et useEffect");
5console.log("Gestion des systèmes quantiques\n");
6
7// ==========================================
8// 1. useState - gestion de l'état
9// ==========================================
10
11function QuantumReactor() {
12 const [temperature, setTemperature] = useState<number>(20);
13 const [pressure, setPressure] = useState<number>(100);
14 const [status, setStatus] = useState<'stable' | 'warning' | 'critical'>('stable');
15
16 // useEffect - surveille les changements de température et de pression
17 useEffect(() => {
18 console.log(`Température : ${temperature}°C, pression : ${pressure} kPa`);
19
20 // Détermine le statut à partir des valeurs
21 if (temperature > 80 || pressure > 150) {
22 setStatus('critical');
23 } else if (temperature > 60 || pressure > 120) {
24 setStatus('warning');
25 } else {
26 setStatus('stable');
27 }
28 }, [temperature, pressure]); // Appelé quand la température ou la pression change
29
30 const getStatusColor = () => {
31 switch(status) {
32 case 'stable': return '#4caf50';
33 case 'warning': return '#ff9800';
34 case 'critical': return '#f44336';
35 }
36 };
37
38 return (
39 <div style={{
40 background: 'rgba(255, 255, 255, 0.1)',
41 padding: '20px',
42 borderRadius: '12px',
43 border: `2px solid ${getStatusColor()}`
44 }}>
45 <h2 style={{ color: getStatusColor() }}>
46 Réacteur quantique - {status.toUpperCase()}
47 </h2>
48
49 <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px', marginTop: '20px' }}>
50 <div>
51 <h4>Température : {temperature}°C</h4>
52 <input
53 type="range"
54 min="0"
55 max="100"
56 value={temperature}
57 onChange={(e) => setTemperature(Number(e.target.value))}
58 style={{ width: '100%' }}
59 />
60 </div>
61
62 <div>
63 <h4>Pression : {pressure} kPa</h4>
64 <input
65 type="range"
66 min="50"
67 max="200"
68 value={pressure}
69 onChange={(e) => setPressure(Number(e.target.value))}
70 style={{ width: '100%' }}
71 />
72 </div>
73 </div>
74 </div>
75 );
76}
77
78// ==========================================
79// 2. useEffect - effets de bord
80// ==========================================
81
82function QuantumClock() {
83 const [time, setTime] = useState<Date>(new Date());
84 const [quantumTime, setQuantumTime] = useState<number>(0);
85
86 // useEffect pour l'horloge - s'exécute après le montage du composant
87 useEffect(() => {
88 console.log("Horloge quantique démarrée");
89
90 const interval = setInterval(() => {
91 setTime(new Date());
92 setQuantumTime(prev => prev + 1);
93 }, 1000);
94
95 // Fonction de nettoyage - appelée au démontage
96 return () => {
97 console.log("Horloge quantique arrêtée");
98 clearInterval(interval);
99 };
100 }, []); // Tableau vide = exécuté une seule fois après le montage
101
102 return (
103 <div style={{
104 background: 'rgba(124, 77, 255, 0.2)',
105 padding: '20px',
106 borderRadius: '12px',
107 textAlign: 'center'
108 }}>
109 <h3 style={{ color: '#7c4dff' }}>Portail temporel quantique</h3>
110 <div style={{ fontSize: '2rem', margin: '20px 0', color: '#64ffda' }}>
111 {time.toLocaleTimeString()}
112 </div>
113 <div style={{ color: '#b0bec5' }}>
114 Secondes quantiques : {quantumTime}
115 </div>
116 </div>
117 );
118}
119
120// ==========================================
121// 3. Plusieurs Hooks useEffect
122// ==========================================
123
124function DataMonitor() {
125 const [data, setData] = useState<number[]>([]);
126 const [average, setAverage] = useState<number>(0);
127 const [trend, setTrend] = useState<'up' | 'down' | 'stable'>('stable');
128
129 // Effet 1 : génère de nouvelles données chaque seconde
130 useEffect(() => {
131 const interval = setInterval(() => {
132 const newValue = Math.floor(Math.random() * 100);
133 setData(prev => [...prev.slice(-9), newValue]); // Garde les 10 dernières valeurs
134 }, 1000);
135
136 return () => clearInterval(interval);
137 }, []);
138
139 // Effet 2 : calcule la moyenne quand les données changent
140 useEffect(() => {
141 if (data.length > 0) {
142 const avg = data.reduce((a, b) => a + b, 0) / data.length;
143 setAverage(avg);
144 console.log(`Nouvelle moyenne : ${avg.toFixed(2)}`);
145 }
146 }, [data]);
147
148 // Effet 3 : détermine la tendance
149 useEffect(() => {
150 if (data.length >= 2) {
151 const lastTwo = data.slice(-2);
152 if (lastTwo[1] > lastTwo[0]) setTrend('up');
153 else if (lastTwo[1] < lastTwo[0]) setTrend('down');
154 else setTrend('stable');
155 }
156 }, [data]);
157
158 const getTrendIcon = () => {
159 switch(trend) {
160 case 'up': return '';
161 case 'down': return '';
162 case 'stable': return '';
163 }
164 };
165
166 return (
167 <div style={{
168 background: 'rgba(255, 255, 255, 0.1)',
169 padding: '20px',
170 borderRadius: '12px'
171 }}>
172 <h3 style={{ color: '#64ffda' }}>Moniteur de flux de données</h3>
173
174 <div style={{ display: 'flex', gap: '5px', marginTop: '15px', height: '100px', alignItems: 'flex-end' }}>
175 {data.map((value, index) => (
176 <div
177 key={index}
178 style={{
179 flex: 1,
180 height: `${value}%`,
181 background: 'linear-gradient(to top, #7c4dff, #64ffda)',
182 borderRadius: '4px 4px 0 0'
183 }}
184 />
185 ))}
186 </div>
187
188 <div style={{ marginTop: '20px', display: 'flex', justifyContent: 'space-around' }}>
189 <div>
190 <div style={{ color: '#b0bec5', fontSize: '0.9rem' }}>Moyenne</div>
191 <div style={{ color: '#64ffda', fontSize: '1.5rem', fontWeight: 'bold' }}>
192 {average.toFixed(1)}
193 </div>
194 </div>
195 <div>
196 <div style={{ color: '#b0bec5', fontSize: '0.9rem' }}>Tendance</div>
197 <div style={{ fontSize: '1.5rem' }}>
198 {getTrendIcon()}
199 </div>
200 </div>
201 </div>
202 </div>
203 );
204}
205
206// ==========================================
207// App principale
208// ==========================================
209function App() {
210 useEffect(() => {
211 console.log("Tableau de bord des systèmes quantiques initialisé");
212 }, []);
213
214 return (
215 <div style={{
216 padding: '20px',
217 background: 'linear-gradient(135deg, #0f0f23 0%, #1a1a2e 100%)',
218 minHeight: '100vh',
219 color: 'white'
220 }}>
221 <h1 style={{ textAlign: 'center', color: '#64ffda' }}>
222 Hooks React - useState et useEffect
223 </h1>
224
225 <div style={{ display: 'grid', gap: '20px', marginTop: '30px' }}>
226 <QuantumReactor />
227 <QuantumClock />
228 <DataMonitor />
229 </div>
230
231 <div style={{
232 marginTop: '30px',
233 background: 'rgba(100, 255, 218, 0.1)',
234 padding: '20px',
235 borderRadius: '10px',
236 borderLeft: '4px solid #64ffda'
237 }}>
238 <h3>Astuces useEffect :</h3>
239 <ul style={{ lineHeight: '1.8' }}>
240 <li>[] (tableau vide) = exécuté une seule fois après le montage</li>
241 <li>[dep1, dep2] = exécuté quand les dépendances changent</li>
242 <li>Sans tableau = exécuté après chaque rendu</li>
243 <li>Fonction retournée = nettoyage avant le démontage</li>
244 <li>Tu peux avoir plusieurs useEffect dans un composant</li>
245 </ul>
246 </div>
247 </div>
248 );
249}
250
251export default App;Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Les Props dans React sont :
2. Le State dans React est :
3. Le hook useState sert à :
Tâches pratiques dans le jeu
- Éditeur de code
Crée un Server Component asynchrone ImplantDashboard (export default). Au-dessus du composant, définis une fonction async getImplants() qui retourne un tableau de 3 implants de la forme { id, name, status }. Dans le composant, récupère les données avec const implants = await getImplants(), puis affiche-les dans un <ul> : chaque implant dans un <li key={implant.id}> avec son nom et son statut.
- Éditeur de code
Crée un Client Component NeuralVisualizer (export default) : ajoute 'use client' en haut du fichier, garde l'activité dans un useState (un booléen, false au départ), ajoute un bouton « Activer Neural Link » qui change l'état, et affiche « Statut : ACTIF » ou « Statut : INACTIF ».
- Clique dans l'ordre
Ordonne la syntaxe pour exporter un Server Component dans Next.js
- Éditeur de code
Crée un composant Greeting qui accepte une prop 'name' et affiche un message de bienvenue personnalisé
- Tri vertical
Ordonne les étapes du rendu SSR d'une page dans Next.js
- Éditeur de code
Crée un composant Counter qui affiche un compteur (useState, valeur initiale 0) et un bouton qui augmente le compteur de 1 à chaque clic.
- Clique dans l'ordre
Ordonne la syntaxe de déstructuration du hook useState
- Éditeur de code
Crée un composant HolographicInterface : exporte une fonction par défaut qui retourne une div avec la classe 'dashboard-panel', un titre h1 'Next.js Dashboard' et un paragraphe avec le statut du système.
- Éditeur de code
Crée un composant de page de connexion LoginPage (export default). Ajoute 'use client' en haut du fichier. Retourne un <form> avec un champ e-mail (<input type="email">), un champ mot de passe (<input type="password">) et un bouton « Se connecter » (<button type="submit">). Dans le gestionnaire onSubmit, appelle event.preventDefault() pour que la page ne se recharge pas.