Kurs Next.js · Moduł 1: Konfiguracja Next.js
System komponentów - Client i Server Components
W tej lekcji10
W Metropolii Quantum architekci systemów kwantowych zawsze muszą podejmować fundamentalne decyzje: które procesy uruchomić na centralnych procesorach kwantowych (dla zadań wymagających najwyższej mocy obliczeniowej), a które na urządzeniach końcowych obywateli (dla natychmiastowej interaktywności). Ta dychotomia doskonale odzwierciedla rewolucyjną zmianę, którą App Router wprowadził w Next.js 13, a Next.js 16 rozwija dalej - podział na Server Components i Client Components.
Rewolucja React Server Components
React Server Components (RSC) stanowią największą zmianę paradygmatu w React od czasu wprowadzenia hooków. Przed ich pojawieniem się wszystkie komponenty React były renderowane na serwerze lub kliencie jako spójna całość. Teraz możemy tworzyć aplikacje, w których niektóre komponenty są renderowane wyłącznie na serwerze, inne na kliencie, a dane i struktury UI płynnie między nimi przepływają.
To jak różnica między tradycyjnymi systemami transportowymi Metropolii Quantum, gdzie pojazdy poruszają się tylko po określonych trasach, a nowym systemem kwantowego transportu, gdzie materia może być teleportowana bezpośrednio do miejsca docelowego.
Server Components - podstawy
W App Routerze Next.js 16 wszystkie komponenty są domyślnie Server Components, chyba że jawnie oznaczymy je jako Client Components.
Server Components są renderowane na serwerze i nie wysyłają żadnego JavaScript do przeglądarki. To jak kwantowe procesory obliczeniowe Metropolii Quantum, które wykonują skomplikowane obliczenia i przesyłają tylko końcowe wyniki do urządzeń obywateli.
Zalety Server Components:
- Dostęp do zasobów serwera - bezpośredni dostęp do baz danych, systemu plików i innych zasobów serwera
- Bezpieczeństwo - wrażliwe dane i logika pozostają na serwerze
- Automatyczny code splitting - tylko niezbędny kod jest wysyłany do klienta
- Mniejszy rozmiar paczek JavaScript - brak potrzeby przesyłania kodu do renderowania
- Lepsze SEO - szybsze renderowanie statyczne
- Lepsza wydajność pierwszego ładowania - mniejsza ilość JavaScript do pobrania i wykonania
Przykład Server Component:
1// app/dashboard/stats/page.tsx - Server Component (domyślnie)
2export default async function StatsPage() {
3 // Ten kod wykonuje się TYLKO na serwerze
4 const stats = await fetchQuantumSystemStats();
5
6 return (
7 <section className="quantum-stats">
8 <h1>Statystyki Systemów Kwantowych</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}Zwróć uwagę na kilka ważnych aspektów:
- Użycie async/await bezpośrednio w komponencie - to możliwe tylko w Server Components
- Brak użycia hooków React (useState, useEffect itp.) - nie są one dostępne w Server Components
- Brak obsługi zdarzeń - Server Components nie mogą obsługiwać zdarzeń użytkownika
Client Components - podstawy
Client Components zapewniają interaktywność: przy pierwszym wejściu Next.js prerenderuje je do HTML na serwerze, a w przeglądarce są hydratowane i dalej działają po stronie klienta. Aby oznaczyć komponent jako Client Component, dodajemy na górze pliku dyrektywę "use client".
Ten proces nazywa się hydratacją: React w przeglądarce podłącza obsługę zdarzeń do HTML-a wyrenderowanego wcześniej na serwerze. Dopiero po hydratacji przyciski reagują na kliknięcia, a efekty z useEffect uruchamiają się po niej.
To jak osobiste urządzenia kwantowe obywateli Metropolii Quantum - lżejsze, ale natychmiastowo reagujące na akcje użytkownika.
Zalety Client Components:
- Interaktywność - obsługa zdarzeń użytkownika (kliknięcia, wprowadzanie tekstu itp.)
- Hooki React - możliwość używania useState, useEffect, useContext i innych hooków
- Dostęp do API przeglądarki - window, document, localStorage itp.
- Efekty po stronie klienta - animacje, interakcje w czasie rzeczywistym
Przykład Client Component:
1// app/dashboard/interactive-controls.tsx
2"use client"; // Ta dyrektywa oznacza komponent kliencki
3
4import { useState, useEffect } from 'react';
5
6export default function InteractiveControls() {
7 // Ten kod wykonuje się w przeglądarce użytkownika
8 const [activeSystem, setActiveSystem] = useState('energy');
9 const [status, setStatus] = useState('idle');
10
11 useEffect(() => {
12 // Efekty działają tylko na kliencie
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 Energia
30 </button>
31 <button onClick={() => setActiveSystem('security')}>
32 Bezpieczeństwo
33 </button>
34 </div>
35 </div>
36 );
37}Kluczowe aspekty:
- Dyrektywa
"use client"na początku pliku - Używanie hooków React (useState, useEffect)
- Obsługa zdarzeń (onClick)
Łączenie Server i Client Components
Najpotężniejszą cechą nowego modelu jest możliwość łączenia Server i Client Components w jednej aplikacji. Server Components mogą renderować Client Components, ale nie odwrotnie (z pewnymi wyjątkami przy użyciu wzorców). Jest to jak hybrydowy system transportu Metropolii Quantum, gdzie główne trasy są obsługiwane przez teleportację kwantową, a ostatni odcinek przez osobiste pojazdy.
Przykład łączenia komponentów:
1// app/dashboard/page.tsx - Server Component
2export default async function DashboardPage() {
3 // Ten kod wykonuje się TYLKO na serwerze
4 const dashboardData = await fetchDashboardData();
5
6 return (
7 <main className="quantum-dashboard">
8 <h1>Panel Kontrolny Metropolii Quantum</h1>
9
10 {/* Server Component */}
11 <SystemStatusPanel systemsData={dashboardData.systems} />
12
13 {/* Client Component (importowany i używany w Server Component) */}
14 <InteractiveControls />
15 </main>
16 );
17}1// app/dashboard/system-status-panel.tsx - Server Component (domyślnie)
2export default function SystemStatusPanel({ systemsData }) {
3 return (
4 <div className="system-status-panel">
5 <h2>Status Systemów</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 (domyślnie)
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>Wydajność: {system.performance}%</p>
9
10 {/* Client Component używany w 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' ? 'Aktywny' : 'Nieaktywny'}
20 </div>
21 );
22}Strategie optymalizacji przy projektowaniu komponentów
Właściwe decydowanie, które komponenty powinny być Server Components, a które Client Components, jest kluczowe dla wydajności aplikacji. Oto kilka strategii, które pomogą Ci podejmować te decyzje:
1. Server Component jako domyślny wybór
Zawsze zaczynaj od Server Components, chyba że potrzebujesz funkcji dostępnych tylko w Client Components. To podobne do podejścia w Metropolii Quantum, gdzie domyślnie używa się centralnych jednostek obliczeniowych, chyba że potrzebna jest natychmiastowa interaktywność.
2. Granularna interaktywność
Zamiast oznaczać cały duży komponent jako "use client", wydziel tylko interaktywne części do osobnych Client Components, pozostawiając resztę jako Server Components.
Przed optymalizacją:
1// MNIEJ OPTYMALNE - cały komponent jest 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>Panel Sterowania</h2>
11 <p>Ten panel pozwala na zarządzanie systemami kwantowymi.</p>
12
13 <div className="tabs">
14 <button onClick={() => setActiveTab('overview')}>Przegląd</button>
15 <button onClick={() => setActiveTab('settings')}>Ustawienia</button>
16 </div>
17
18 <div className="tab-content">
19 {activeTab === 'overview' ? (
20 <OverviewContent /> // Dużo statycznej zawartości
21 ) : (
22 <SettingsContent /> // Dużo statycznej zawartości
23 )}
24 </div>
25 </div>
26 );
27}Po optymalizacji:
1// LEPSZE - rozdzielenie na Client i 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>Panel Sterowania</h2>
12 <p>Ten panel pozwala na zarządzanie systemami kwantowymi.</p>
13
14 {/* Tylko TabController jest Client Component; zawartość zakładek zostaje na serwerze */}
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')}>Przegląd</button>
31 <button onClick={() => setActiveTab('settings')}>Ustawienia</button>
32 </div>
33
34 <div className="tab-content">
35 {activeTab === 'overview' ? overview : settings}
36 </div>
37 </>
38 );
39}Komponent zaimportowany w pliku z "use client" też staje się Client Component. Dlatego OverviewContent i SettingsContent importuje Server Component ControlPanel i przekazuje je do TabController jako propsy, tak jak children.
3. Wzorzec "Kontener i Prezentacja"
Użyj Server Components jako "kontenerów" do pobierania danych i organizacji, a Client Components jako "komponentów prezentacyjnych" odpowiedzialnych za interaktywność.
1// products-container.tsx - Server Component (kontener)
2export default async function ProductsContainer() {
3 const products = await fetchProducts();
4
5 return <ProductsList products={products} />;
6}
7
8// products-list.tsx - Client Component (prezentacja)
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')}>Sortuj po nazwie</button>
22 <button onClick={() => setSortBy('price')}>Sortuj po cenie</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}Przepływ danych między Server i Client Components
Przesyłanie danych między serwerem a klientem wymaga specjalnego podejścia. W modelu RSC props płyną w drzewie komponentów z serwera do klienta; w drugą stronę klient wysyła dane, wywołując Server Actions (poznasz je za chwilę). To jak kanały kwantowe w Metropolii Quantum - rozkazy płyną z centrali do urządzeń osobistych, a meldunki wracają osobnym kanałem.
Przekazywanie danych do Client Components:
1// server-component.tsx - Server Component
2async function ServerComponent() {
3 const data = await fetchSensitiveData();
4
5 // Przekazanie przetworzonych danych do klienta
6 return <ClientComponent processedData={data.publicPart} />;
7}
8
9// client-component.tsx - Client Component
10"use client";
11
12export function ClientComponent({ processedData }) {
13 // Używanie danych przekazanych z serwera
14 return <div>{processedData.map(item => <p key={item.id}>{item.name}</p>)}</div>;
15}Obsługa akcji użytkownika z Client Components:
Gdy Client Component musi wykonać akcję, która angażuje serwer, używamy Server Actions albo Route Handlers (plików route.ts; w Pages Routerze ich odpowiednikiem są API Routes).
Z użyciem Server Actions (stabilnych od Next.js 14):
1// actions.js - Server Actions
2"use server";
3
4export async function updateSystemStatus(systemId, newStatus) {
5 // Ta funkcja działa na serwerze
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' ? 'Dezaktywuj' : 'Aktywuj'}
30 </button>
31 );
32}Różnice w przeładowaniach i nawigacji
W tradycyjnym modelu React przeładowanie strony lub nawigacja często oznaczały ponowne uruchomienie całej aplikacji. W modelu React Server Components nawigacja jest wydajniejsza:
- Server Components są ponownie renderowane na serwerze podczas nawigacji
- Client Components we wspólnych layoutach zachowują stan podczas nawigacji; komponenty opuszczanej strony są domyślnie odmontowywane
- Przejścia między stronami są płynniejsze dzięki mechanizmowi "instant navigation"
Jest to jak zaawansowane przełączniki kwantowe w Metropolii Quantum - pozwalają na natychmiastowe przejścia między różnymi układami bez przerywania działania systemów.
Typowe błędy i pułapki
1. Użycie hooków React w Server Components
1// ŹLE - Server Component próbuje używać hooków React
2export default function ServerComponent() {
3 // To spowoduje błąd!
4 const [count, setCount] = useState(0);
5
6 return <div>Count: {count}</div>;
7}2. Próba dostępu do API przeglądarki w Server Components
1// ŹLE - Server Component próbuje używać API przeglądarki
2export default function ServerComponent() {
3 // To spowoduje błąd! (ReferenceError: window is not defined)
4 const windowWidth = window.innerWidth;
5
6 return <div>Width: {windowWidth}px</div>;
7}3. Przekazywanie funkcji z Server Components do Client Components
1// ŹLE - Server Component przekazuje zwykłą funkcję do 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 // To nie zadziała! Props przekazywane do Client Component muszą dać się zserializować
9 return <ClientButton onClick={handleClick} />;
10}Wyjątkiem są Server Actions: funkcję oznaczoną "use server" możesz przekazać do Client Component.
Zaawansowane wzorce używania Server i Client Components
1. Wzorzec Providera
Jeśli potrzebujesz współdzielić stan między różnymi Client Components, używaj Client Providera na wysokim poziomie. Jest to podobne do centralnych jednostek synchronizacyjnych w Metropolii Quantum, które koordynują działania różnych urządzeń osobistych.
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. Wzorzec "podnoszenia stanu" (lifting state up)
Kiedy masz kilka Client Components, które potrzebują dostępu do tego samego stanu, podnieś stan do wspólnego przodka Client Component, a nie do Server Component.
1// parent.tsx - Server Component
2export default function ParentServerComponent() {
3 return <ClientParent />;
4}
5
6// client-parent.tsx - Client Component (wspólny przodek)
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. Strategiczne użycie suspense
Użyj komponentu Suspense do zarządzania stanami ładowania w aplikacji hybrydowej:
1// page.tsx - Server Component
2import { Suspense } from 'react';
3
4export default function Page() {
5 return (
6 <div>
7 <h1>Dane systemowe Metropolii Quantum</h1>
8
9 <Suspense fallback={<div>Ładowanie danych...</div>}>
10 <AsyncDataComponent />
11 </Suspense>
12 </div>
13 );
14}
15
16// async-data-component.tsx - Server Component z asynchronicznym kodem
17async function AsyncDataComponent() {
18 // Ten komponent może być zawieszony (suspendowany) podczas ładowania
19 const data = await fetchData();
20
21 return <div>{/* Renderowanie danych */}</div>;
22}Podsumowanie
System komponentów w Next.js 16 oparty na React Server Components to rewolucyjna zmiana, która łączy zalety renderowania serwerowego z interaktywnością kliencką. Podobnie jak w Metropolii Quantum, gdzie kwantowa teleportacja i osobiste pojazdy tworzą hybrydowy system transportu, tak w Next.js komponenty serwerowe i klienckie współpracują, tworząc wydajny, skalowalny i przyjazny dla użytkownika interfejs.
Przy projektowaniu aplikacji Next.js 16:
- Domyślnie używaj Server Components dla lepszej wydajności i bezpieczeństwa
- Przełącz na Client Components tylko wtedy, gdy potrzebujesz interaktywności, hooków lub API przeglądarki
- Rozdzielaj komponenty granularnie, aby zminimalizować ilość kodu JavaScript wysyłanego do klienta
- Pamiętaj o kierunku przepływu props - z serwera do klienta (dane w drugą stronę wysyłasz przez Server Actions)
- Używaj Server Actions do przetwarzania formularzy i innych akcji użytkownika
Prawidłowe wykorzystanie Server i Client Components pozwoli Ci tworzyć aplikacje Next.js nowej generacji, które łączą szybkość ładowania, wydajność SEO i bogaty interfejs użytkownika - bez kompromisów, które były konieczne w tradycyjnych aplikacjach React.
W następnej lekcji przejdziemy do praktyki i stworzymy naszą pierwszą aplikację "Hello World" z użyciem Next.js 16 i App Routera, gdzie zastosujemy zdobytą wiedzę o Server i Client Components.
Kod do tej lekcji: App.tsx
1// Hooki - useState i useEffect - Metropolis Quantum
2import React, { useState, useEffect } from 'react';
3
4console.log("Hooki Reacta - useState i useEffect");
5console.log("Zarządzanie systemami kwantowymi\n");
6
7// ==========================================
8// 1. useState - Zarządzanie Stanem
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 - monitoruj zmiany temperatury i ciśnienia
17 useEffect(() => {
18 console.log(`Temperatura: ${temperature}°C, Ciśnienie: ${pressure} kPa`);
19
20 // Określ status na podstawie wartości
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]); // Wywołaj, gdy zmieni się temperatura lub ciśnienie
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 Reaktor kwantowy - {status.toUpperCase()}
47 </h2>
48
49 <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px', marginTop: '20px' }}>
50 <div>
51 <h4>Temperatura: {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>Ciśnienie: {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 - efekty uboczne
80// ==========================================
81
82function QuantumClock() {
83 const [time, setTime] = useState<Date>(new Date());
84 const [quantumTime, setQuantumTime] = useState<number>(0);
85
86 // useEffect dla zegara - uruchamia się po zamontowaniu komponentu
87 useEffect(() => {
88 console.log("Zegar kwantowy uruchomiony");
89
90 const interval = setInterval(() => {
91 setTime(new Date());
92 setQuantumTime(prev => prev + 1);
93 }, 1000);
94
95 // Funkcja sprzątająca - wywołana przy odmontowaniu
96 return () => {
97 console.log("Zegar kwantowy zatrzymany");
98 clearInterval(interval);
99 };
100 }, []); // Pusta tablica = uruchom raz po zamontowaniu
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' }}>Kwantowy portal czasu</h3>
110 <div style={{ fontSize: '2rem', margin: '20px 0', color: '#64ffda' }}>
111 {time.toLocaleTimeString()}
112 </div>
113 <div style={{ color: '#b0bec5' }}>
114 Sekundy kwantowe: {quantumTime}
115 </div>
116 </div>
117 );
118}
119
120// ==========================================
121// 3. Kilka hooków 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 // Efekt 1: generuj nowe dane co sekundę
130 useEffect(() => {
131 const interval = setInterval(() => {
132 const newValue = Math.floor(Math.random() * 100);
133 setData(prev => [...prev.slice(-9), newValue]); // Zachowaj ostatnie 10 wartości
134 }, 1000);
135
136 return () => clearInterval(interval);
137 }, []);
138
139 // Efekt 2: oblicz średnią, gdy dane się zmienią
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(`Nowa średnia: ${avg.toFixed(2)}`);
145 }
146 }, [data]);
147
148 // Efekt 3: określ trend
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' }}>Monitor strumienia danych</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' }}>Średnia</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' }}>Trend</div>
197 <div style={{ fontSize: '1.5rem' }}>
198 {getTrendIcon()}
199 </div>
200 </div>
201 </div>
202 </div>
203 );
204}
205
206// ==========================================
207// Główna aplikacja
208// ==========================================
209function App() {
210 useEffect(() => {
211 console.log("Panel systemów kwantowych zainicjalizowany");
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 Hooki Reacta - useState i 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>Wskazówki do useEffect:</h3>
239 <ul style={{ lineHeight: '1.8' }}>
240 <li>[] (pusta tablica) = uruchom raz po zamontowaniu</li>
241 <li>[dep1, dep2] = uruchom, gdy zmienią się zależności</li>
242 <li>Bez tablicy = uruchom po każdym renderze</li>
243 <li>Zwracana funkcja = sprzątanie przed odmontowaniem</li>
244 <li>Możesz mieć wiele useEffect w komponencie</li>
245 </ul>
246 </div>
247 </div>
248 );
249}
250
251export default App;Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Props w React to:
2. State w React to:
3. Hook useState służy do:
Zadania praktyczne w grze
- Edytor kodu
Stwórz async Server Component ImplantDashboard (export default). Nad komponentem zdefiniuj funkcję async getImplants(), która zwraca tablicę 3 implantów w postaci { id, name, status }. W komponencie pobierz dane: const implants = await getImplants(), a potem wyświetl je w <ul>: każdy implant jako <li key={implant.id}> z nazwą i statusem.
- Edytor kodu
Stwórz Client Component NeuralVisualizer (export default): na początku pliku dodaj 'use client', aktywność trzymaj w useState (boolean, początkowo false), dodaj przycisk „Aktywuj Neural Link”, który zmienia stan, i wyświetl „Status: AKTYWNY” albo „Status: NIEAKTYWNY”.
- Klikanie w kolejności
Ułóż składnię eksportu Server Component w Next.js
- Edytor kodu
Utwórz komponent Greeting, który przyjmuje prop 'name' i wyświetla spersonalizowane powitanie
- Układanie w pionie
Ułóż kolejność kroków renderowania strony SSR w Next.js
- Edytor kodu
Utwórz komponent Counter, który wyświetla licznik (useState, wartość początkowa 0) i przycisk zwiększający licznik o 1 przy każdym kliknięciu.
- Klikanie w kolejności
Ułóż składnię destrukturyzacji hooka useState
- Edytor kodu
Stwórz komponent HolographicInterface: eksportuj domyślną funkcję zwracającą div z klasą 'dashboard-panel', nagłówkiem h1 'Next.js Dashboard' i paragrafem ze statusem systemu.
- Edytor kodu
Stwórz komponent strony logowania LoginPage (export default). Na początku pliku dodaj 'use client'. Zwróć formularz <form> z polem e-mail (<input type="email">), polem hasła (<input type="password">) i przyciskiem „Zaloguj” (<button type="submit">). W obsłudze onSubmit wywołaj event.preventDefault(), żeby strona się nie przeładowała.