Kurs JavaScript i React · Moduł 1: Podstawy React

Składnia JSX

5 min czytania
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 -> onClick
  • tabindex -> tabIndex
  • class -> 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 zdefiniowany

3. 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. 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:

Przydatne artykuły