Kurs JavaScript i React · Moduł 1: Podstawy React
Składnia JSX
W tej lekcji5
JSX (JavaScript XML) to rozszerzenie składni JavaScript, które pozwala pisać kod przypominający HTML bezpośrednio w JavaScript. Jest to deklaratywny sposób opisywania, jak powinna wyglądać struktura UI w React.
Czym dokładnie jest JSX?
Dla programisty, który dopiero zaczyna przygodę z React, JSX może wydawać się dziwny - jak to możliwe, że nagle w środku JavaScriptu pojawiają się znaczniki HTML?
Wyobraź sobie JSX jako tłumacza między światem JavaScript a światem HTML. Pozwala opisać strukturę UI w sposób, który jest czytelny dla ludzi (podobny do HTML), ale jednocześnie zachowuje całą moc JavaScript.
Jak JSX działa pod maską?
Ważne jest, aby zrozumieć, że przeglądarki nie rozumieją JSX natywnie. Zanim kod trafi do przeglądarki, jest kompilowany do zwykłego JavaScriptu przez narzędzia takie jak Babel.
Przykładowo, ten kod JSX:
1const element = <h1>Witaj, świecie!</h1>;Jest transformowany na:
1const element = React.createElement(
2 'h1',
3 null,
4 'Witaj, świecie!'
5);Właśnie dlatego w starszych wersjach Reacta (do 16) trzeba było importować React w każdym pliku z JSX. Od Reacta 17 narzędzia takie jak Vite używają nowego przekształcenia JSX: kompilator sam importuje potrzebne funkcje z pakietu react/jsx-runtime, więc import React from 'react' nie jest już potrzebny.
Zasady JSX
JSX może wydawać się jak HTML, ale ma kilka unikalnych cech i zasad, które warto zrozumieć:
1. Nazwy atrybutów są w camelCase
W przeciwieństwie do HTML, gdzie atrybuty są pisane małymi literami, w JSX wiele atrybutów jest zapisywanych w formacie camelCase:
1// HTML
2<div onclick="handleClick()" tabindex="0" class="container">
3
4// JSX
5<div onClick={handleClick} tabIndex={0} className="container">Zwróć uwagę na różnice:
onclick->onClicktabindex->tabIndexclass->className(ponieważ "class" jest zarezerwowanym słowem w JavaScript)
2. Wartości atrybutów są wyrażeniami JavaScript
W JSX, wartości atrybutów nie są zawsze stringami. Możesz używać dowolnych wyrażeń JavaScript, ujmując je w nawiasy klamrowe:
1// Wartość liczbowa
2<input maxLength={10} />
3
4// Wyrażenie
5<img src={user.avatarUrl} />
6
7// Ternary operator (operator warunkowy)
8<div className={isActive ? 'active' : 'inactive'}>
9
10// Funkcja
11<button onClick={handleClick}>3. Samozamykające się tagi muszą mieć ukośnik
W JSX, tagi bez dzieci (children) muszą mieć ukośnik zamykający:
1// HTML
2<input type="text">
3
4// JSX
5<input type="text" />4. Wszystkie tagi muszą być zamknięte
W HTML niektóre tagi mogą pozostać niezamknięte (np. <img>, <br>), ale w JSX wszystkie tagi muszą być prawidłowo zamknięte:
1// Niepoprawne w JSX
2<img src="logo.png">
3
4// Poprawne w JSX
5<img src="logo.png" />5. Element musi być opakowany w pojedynczy element nadrzędny
JSX wymaga, aby zwracać tylko jeden element nadrzędny. Jeśli chcesz zwrócić wiele elementów, musisz je opakować w kontener:
1// Niepoprawne w JSX
2function Component() {
3 return (
4 <h1>Tytuł</h1>
5 <p>Paragraf</p>
6 );
7}
8
9// Poprawne w JSX
10function Component() {
11 return (
12 <div>
13 <h1>Tytuł</h1>
14 <p>Paragraf</p>
15 </div>
16 );
17}Jeśli nie chcesz wprowadzać dodatkowego elementu DOM, możesz użyć Fragment:
1function Component() {
2 return (
3 <>
4 <h1>Tytuł</h1>
5 <p>Paragraf</p>
6 </>
7 );
8}6. JavaScript w JSX
Możesz używać wyrażeń JavaScript w JSX, umieszczając je w nawiasach klamrowych {}:
1function Greeting({ user }) {
2 return (
3 <div>
4 {/* Wyrażenie warunkowe */}
5 {user ? <h1>Witaj, {user.name}!</h1> : <h1>Witaj, nieznajomy!</h1>}
6
7 {/* Wykonywanie obliczeń */}
8 <p>Twoje konto zostało utworzone {calculateDaysAgo(user.createdAt)} dni temu</p>
9
10 {/* Mapowanie listy */}
11 <ul>
12 {user.hobbies.map(hobby => (
13 <li key={hobby.id}>{hobby.name}</li>
14 ))}
15 </ul>
16 </div>
17 );
18}Zaawansowane techniki JSX
Spread operatory dla props
Możesz używać spread operatora (...), aby przekazać wszystkie właściwości obiektu jako props:
1const buttonProps = {
2 type: 'submit',
3 disabled: false,
4 className: 'primary-button'
5};
6
7<button {...buttonProps}>Wyślij</button>Dynamiczne atrybuty
Możesz dynamicznie określać atrybuty elementów:
1const inputType = isPassword ? 'password' : 'text';
2<input type={inputType} />Warunkowe renderowanie
JSX oferuje kilka sposobów na warunkowe renderowanie elementów:
1// 1. Używając operatora trójskładnikowego
2{isLoggedIn ? <UserProfile /> : <LoginButton />}
3
4// 2. Używając operatora logicznego &&
5{isLoggedIn && <WelcomeMessage />}
6
7// 3. Używając zmiennej
8let button;
9if (isLoggedIn) {
10 button = <LogoutButton />;
11} else {
12 button = <LoginButton />;
13}
14return <div>{button}</div>;Inline styles
W JSX, style inline są definiowane jako obiekty JavaScript:
1const divStyle = {
2 color: 'blue',
3 backgroundColor: 'lightgray',
4 padding: '10px',
5 // Zauważ, że właściwości z myślnikiem (jak background-color)
6 // są zapisywane w camelCase (backgroundColor)
7};
8
9<div style={divStyle}>Stylizowany tekst</div>
10
11// Albo bezpośrednio w JSX
12<div style={{ fontWeight: 'bold', fontSize: '16px' }}>Pogrubiony tekst</div>Komentarze w JSX
Komentarze w JSX są umieszczane wewnątrz nawiasów klamrowych i używają składni komentarzy JavaScript:
1<div>
2 {/* To jest komentarz w JSX */}
3 <h1>Tytuł</h1>
4 {/*
5 Możesz również używać
6 komentarzy wieloliniowych
7 */}
8</div>Typowe pułapki JSX
1. Renderowanie logicznych wartości
JSX nie renderuje logicznych wartości, null i undefined:
1// Te wyrażenia nie renderują nic na ekranie
2<div>{true}</div>
3<div>{false}</div>
4<div>{null}</div>
5<div>{undefined}</div>
6
7// Ale liczby i stringi są renderowane
8<div>{0}</div>
9<div>{"string"}</div>2. Mylenie cudzysłowów i nawiasów klamrowych
Pamiętaj, że cudzysłowy są używane dla wartości łańcuchowych, a nawiasy klamrowe dla wyrażeń JavaScript:
1// Poprawnie
2<input type="text" maxLength={10} />
3
4// Niepoprawnie
5<input type={text} maxLength="10" /> // błąd: 'text' nie jest zdefiniowany3. Próba użycia if/else bezpośrednio w JSX
Nie możesz używać instrukcji if/else bezpośrednio w JSX, ponieważ JSX jest wyrażeniem, a nie instrukcją:
1// Niepoprawne
2<div>
3 {if (isLoggedIn) {
4 return <UserProfile />;
5 } else {
6 return <LoginButton />;
7 }}
8</div>
9
10// Poprawne - użyj operatora trójskładnikowego
11<div>
12 {isLoggedIn ? <UserProfile /> : <LoginButton />}
13</div>Podsumowanie
JSX jest jednym z najważniejszych aspektów React, który znacznie ułatwia tworzenie interfejsów użytkownika. Dzięki JSX, możesz:
- Pisać strukturę UI w sposób przypominający HTML
- Łączyć znaczniki z logiką JavaScript
- Tworzyć komponenty, które są czytelne i łatwe w utrzymaniu
- Korzystać z pełnej mocy JavaScript do dynamicznego renderowania treści
Pamiętaj, że JSX to tylko składnia, która jest przekształcana do zwykłych wywołań funkcji: React.createElement() albo, w nowym przekształceniu, jsx() z react/jsx-runtime. Zrozumienie zasad i możliwości JSX jest kluczowe dla efektywnego programowania w React.
Kod do tej lekcji: App.jsx
1import React, { useState } from 'react';
2
3// Demonstracja różnych aspektów JSX
4export default function App() {
5 const [count, setCount] = useState(0);
6 const [showAdvanced, setShowAdvanced] = useState(false);
7
8 // Zmienne używane w JSX
9 const userName = "Kosmiczny Odkrywca";
10 const currentDate = new Date().toLocaleDateString('pl-PL');
11 const items = ['Mapa galaktyki', 'Kompas kwantowy', 'Skaner życia'];
12 const isLoggedIn = true;
13
14 // Style jako obiekt JavaScript
15 const headerStyle = {
16 background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
17 padding: '30px',
18 borderRadius: '16px',
19 color: 'white',
20 textAlign: 'center',
21 marginBottom: '30px'
22 };
23
24 return (
25 <div style={{
26 fontFamily: 'Arial, sans-serif',
27 maxWidth: '900px',
28 margin: '0 auto',
29 padding: '20px',
30 backgroundColor: '#0a0a1a',
31 minHeight: '100vh',
32 color: 'white'
33 }}>
34 {/* Komentarz w JSX */}
35 <header style={headerStyle}>
36 <h1 style={{ margin: 0 }}>JSX w Akcji</h1>
37 <p style={{ margin: '10px 0 0 0', opacity: 0.9 }}>
38 Poznaj składnię JSX - serce React
39 </p>
40 </header>
41
42 {/* Wyrażenia JavaScript w JSX */}
43 <section style={{
44 backgroundColor: '#1a1a2e',
45 padding: '25px',
46 borderRadius: '12px',
47 marginBottom: '20px'
48 }}>
49 <h2 style={{ color: '#00d4ff', marginTop: 0 }}>
50 Wyrażenia JavaScript w JSX
51 </h2>
52 <div style={{ display: 'grid', gap: '15px' }}>
53 <div style={{ padding: '15px', backgroundColor: '#252545', borderRadius: '8px' }}>
54 <code style={{ color: '#888' }}>{'{'} userName {'}'}</code>
55 <p style={{ margin: '10px 0 0 0' }}>Wynik: <strong>{userName}</strong></p>
56 </div>
57 <div style={{ padding: '15px', backgroundColor: '#252545', borderRadius: '8px' }}>
58 <code style={{ color: '#888' }}>{'{'} 2 + 2 {'}'}</code>
59 <p style={{ margin: '10px 0 0 0' }}>Wynik: <strong>{2 + 2}</strong></p>
60 </div>
61 <div style={{ padding: '15px', backgroundColor: '#252545', borderRadius: '8px' }}>
62 <code style={{ color: '#888' }}>{'{'} currentDate {'}'}</code>
63 <p style={{ margin: '10px 0 0 0' }}>Wynik: <strong>{currentDate}</strong></p>
64 </div>
65 </div>
66 </section>
67
68 {/* Renderowanie warunkowe */}
69 <section style={{
70 backgroundColor: '#1a1a2e',
71 padding: '25px',
72 borderRadius: '12px',
73 marginBottom: '20px'
74 }}>
75 <h2 style={{ color: '#00d4ff', marginTop: 0 }}>
76 Renderowanie warunkowe
77 </h2>
78
79 {/* Operator && */}
80 {isLoggedIn && (
81 <div style={{
82 padding: '15px',
83 backgroundColor: '#4caf50',
84 borderRadius: '8px',
85 marginBottom: '15px'
86 }}>
87 Jesteś zalogowany jako: {userName}
88 </div>
89 )}
90
91 {/* Operator trójargumentowy */}
92 <div style={{
93 padding: '15px',
94 backgroundColor: count > 5 ? '#ff9800' : '#2196f3',
95 borderRadius: '8px'
96 }}>
97 {count > 5 ? 'Licznik jest wysoki!' : 'Licznik jest niski'}
98 <div style={{ marginTop: '10px' }}>
99 <button
100 onClick={() => setCount(count + 1)}
101 style={{
102 padding: '8px 16px',
103 marginRight: '10px',
104 backgroundColor: '#4caf50',
105 color: 'white',
106 border: 'none',
107 borderRadius: '4px',
108 cursor: 'pointer'
109 }}
110 >
111 +1 (teraz: {count})
112 </button>
113 <button
114 onClick={() => setCount(0)}
115 style={{
116 padding: '8px 16px',
117 backgroundColor: '#f44336',
118 color: 'white',
119 border: 'none',
120 borderRadius: '4px',
121 cursor: 'pointer'
122 }}
123 >
124 Reset
125 </button>
126 </div>
127 </div>
128 </section>
129
130 {/* Mapowanie list */}
131 <section style={{
132 backgroundColor: '#1a1a2e',
133 padding: '25px',
134 borderRadius: '12px',
135 marginBottom: '20px'
136 }}>
137 <h2 style={{ color: '#00d4ff', marginTop: 0 }}>
138 Renderowanie list z .map()
139 </h2>
140 <ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
141 {items.map((item, index) => (
142 <li key={index} style={{
143 padding: '12px 15px',
144 backgroundColor: '#252545',
145 borderRadius: '8px',
146 marginBottom: '10px',
147 display: 'flex',
148 alignItems: 'center',
149 gap: '10px'
150 }}>
151 <span style={{
152 width: '30px',
153 height: '30px',
154 backgroundColor: '#667eea',
155 borderRadius: '50%',
156 display: 'flex',
157 alignItems: 'center',
158 justifyContent: 'center',
159 fontWeight: 'bold'
160 }}>
161 {index + 1}
162 </span>
163 {item}
164 </li>
165 ))}
166 </ul>
167 </section>
168
169 {/* Pokaż/ukryj zaawansowane */}
170 <button
171 onClick={() => setShowAdvanced(!showAdvanced)}
172 style={{
173 width: '100%',
174 padding: '15px',
175 backgroundColor: '#667eea',
176 color: 'white',
177 border: 'none',
178 borderRadius: '8px',
179 cursor: 'pointer',
180 fontSize: '16px',
181 fontWeight: 'bold'
182 }}
183 >
184 {showAdvanced ? 'Ukryj' : 'Pokaż'} zaawansowane przykłady
185 </button>
186
187 {showAdvanced && (
188 <section style={{
189 backgroundColor: '#1a1a2e',
190 padding: '25px',
191 borderRadius: '12px',
192 marginTop: '20px'
193 }}>
194 <h2 style={{ color: '#ff9800', marginTop: 0 }}>
195 Zaawansowane JSX
196 </h2>
197 <div style={{
198 backgroundColor: '#252545',
199 padding: '15px',
200 borderRadius: '8px',
201 fontFamily: 'monospace',
202 fontSize: '14px',
203 lineHeight: '1.6'
204 }}>
205 <div style={{ color: '#888' }}>// Fragment - grupowanie bez dodatkowego DOM</div>
206 <div style={{ color: '#61dafb' }}>{'<>'}</div>
207 <div style={{ color: '#ce9178', paddingLeft: '20px' }}>{' <Element1 />'}</div>
208 <div style={{ color: '#ce9178', paddingLeft: '20px' }}>{' <Element2 />'}</div>
209 <div style={{ color: '#61dafb' }}>{'</>'}</div>
210 </div>
211 </section>
212 )}
213 </div>
214 );
215}Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Jaka jest jedna z kluczowych zasad składni JSX?
Zadania praktyczne w grze
- Układanie w poziomie
Ułóż składnię poprawnego elementu JSX z lewej do prawej:
- Edytor kodu
Stwórz ControlPanel z: nagłówkiem h1 'Panel Kontrolny', listą <ul> 3 systemów (używając map), i przyciskiem 'Aktywuj' z onClick. Użyj className dla stylowania.
- Edytor kodu
Bez używania JSX: w komponencie App utwórz za pomocą React.createElement element komponentu SpaceshipPart z właściwościami name i type (np. name: 'Silnik', type: 'napęd') i zwróć go. Sam SpaceshipPart niech wyświetla nazwę i typ części.
- Klikanie w kolejności
Zbuduj poprawne wyrażenie JavaScript osadzone w elemencie JSX:
- Układanie w poziomie
Ułóż składnię React.createElement tworząc element h1 z tekstem:
- Układanie w poziomie
Ułóż składnię renderowania komponentu App do DOM w pliku main.jsx (React 18+):
- Edytor kodu
Stwórz komponent CrewMembers, który zwraca kilka elementów (np. <li> dla każdego członka załogi) zgrupowanych we Fragmencie (<></> lub <React.Fragment>) zamiast w dodatkowym <div>.
- Układanie w pionie
Ułóż proces kompilacji JSX do DOM:
- Edytor kodu
Owiń główny komponent App w React.StrictMode aby włączyć dodatkowe sprawdzenia podczas developmentu.
- Układanie w poziomie
Ułóż poprawną składnię importu biblioteki ReactDOM do renderowania w przeglądarce:
- Edytor kodu
Użyj ReactDOM.createRoot, aby wyrenderować komponent SpaceStation w elemencie DOM o id 'space-station' (tak jak w pliku main.jsx z lekcji o Vite).
- Edytor kodu
Stwórz komponent SystemStatus przyjmujący props: {name, status, power}. Wyświetl: nazwę systemu, status (online/offline) i zużycie energii.
- Układanie w poziomie
Ułóż poprawną składnię importu biblioteki React: