Opisywanie UI

React jest biblioteką javascriptową służącą do renderowania interfejsu użytkownika (UI). UI jest zbudowany z małych części, np. przyciski, tekst czy obrazki. React pozwala zgrupować je w komponenty, które można zagnieżdżać i używać ich wielokrotnie. Zarówno na stronach internetowych, jak i w aplikacjach mobilnych, wszystko, co znajduje się na ekranie, można rozbić na osobne komponenty. W tym rozdziale nauczysz się tworzyć, modyfikować i wyświetlać warunkowo komponenty reactowe.

Twój pierwszy komponent

Aplikacje reactowe buduje się z odizolowanych od siebie kawałków UI zwanych komponentami. Komponent reactowy jest funkcją javascriptową, którą można “okrasić” znacznikami. Komponentem może być pojedynczy przycisk, ale także cała strona. Oto przykład komponentu o nazwie Gallery, renderującego komponenty Profile:

function Profile() {
  return (
    <img
      src="https://i.imgur.com/MK3eW3As.jpg"
      alt="Katherine Johnson"
    />
  );
}

export default function Gallery() {
  return (
    <section>
      <h1>Niesamowici naukowcy</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

Chcesz zgłębić ten temat?

Aby dowiedzieć się, jak deklarować i używać komponentów reactowych, przeczytaj rozdział pt. Twój pierwszy komponent.

Czytaj dalej

Importowanie i eksportowanie komponentów

W jednym pliku możesz zadeklarować wiele komponentów naraz, lecz duże pliki są trudne w czytaniu i utrzymaniu. Aby poradzić sobie z tym problemem, możesz wyeksportować komponent z jednego pliku, a następnie zaimportować go w innym:

import Profile from './Profile.js';

export default function Gallery() {
  return (
    <section>
      <h1>Niesamowici naukowcy</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

Chcesz zgłębić ten temat?

Aby dowiedzieć się więcej o wydzielaniu komponentów do osobnych plików, przeczytaj rozdział pt. Importowanie i eksportowanie komponentów.

Czytaj dalej

Dodawanie znaczników w JSX

Każdy komponent reactowy jest funkcją javascriptową, która może zawierać w sobie kod znaczników renderowany do przeglądarki. Komponenty reactowe używają rozszerzenia składni zwanego JSX w celu przedstawienia tego kodu znaczników. JSX wygląda bardzo podobnie do HTML-a, jednak jest nieco bardziej restrykcyjny i potrafi wyświetlać dynamiczne dane.

Jeśli tak po prostu wkleimy istniejący kod HTML do komponentu reactowego, nie zawsze będzie on działał:

export default function TodoList() {
  return (
    // Nie do końca to zadziała!
    <h1>Lista zadań Hedy Lamarr</h1>
    <img
      src="https://i.imgur.com/yXOvdOSs.jpg"
      alt="Hedy Lamarr"
      class="photo"
    >
    <ul>
      <li>Wynaleźć nową sygnalizację świetlną
      <li>Przećwiczyć scenę do filmu
      <li>Usprawnić technologię rozpraszania widma
    </ul>

Jeśli posiadasz już kod HTML, możesz go przekształcić automatycznie za pomocą konwertera:

export default function TodoList() {
  return (
    <>
      <h1>Lista zadań Hedy Lamarr</h1>
      <img
        src="https://i.imgur.com/yXOvdOSs.jpg"
        alt="Hedy Lamarr"
        className="photo"
      />
      <ul>
        <li>Wynaleźć nową sygnalizację świetlną</li>
        <li>Przećwiczyć scenę do filmu</li>
        <li>Usprawnić technologię rozpraszania widma</li>
      </ul>
    </>
  );
}

Chcesz zgłębić ten temat?

Aby dowiedzieć się jak poprawnie pisać kod JSX-owy, przeczytaj rozdział pt. Pisanie kodu w JSX.

Czytaj dalej

JavaScript w JSX a nawiasy klamrowe

JSX pozwala na pisanie kodu podobnego do HTML-a wewnątrz pliku javascriptowego, umożliwiając trzymanie logiki renderowania i treści jednym miejscu. Czasem jednak zachodzi potrzeba, by w kodzie znaczników dodać nieco logiki javascriptowej lub odnieść się do dynamicznej własności. W takiej sytuacji możemy użyć nawiasów klamrowych, otwierając tym samym okno do świata JavaScriptu:

const person = {
  name: 'Gregorio Y. Zara',
  theme: {
    backgroundColor: 'black',
    color: 'pink'
  }
};

export default function TodoList() {
  return (
    <div style={person.theme}>
      <h1>{person.name} - lista zadań</h1>
      <img
        className="avatar"
        src="https://i.imgur.com/7vQD0fPs.jpg"
        alt="Gregorio Y. Zara"
      />
      <ul>
        <li>Usprawnić wideotelefon</li>
        <li>Przygotować wykłady o aeronautyce</li>
        <li>Opracować silnik napędzany alkoholem</li>
      </ul>
    </div>
  );
}

Chcesz zgłębić ten temat?

Aby dowiedzieć się więcej na temat dostępu do danych javascriptowych z poziomu kodu JSX, przeczytaj rozdział pt. JavaScript w JSX a nawiasy klamrowe.

Czytaj dalej

Przekazywanie wartości do komponentu

Komponenty reactowe komunikują się ze sobą za pomocą właściwości (ang. props). Każdy komponent-rodzic może przekazać informacje do swoich potomków właśnie poprzez właściwości. Właściwości mogą kojarzyć ci się z atrybutami HTML-owymi, jednak przez właściwości możesz przekazać dowolną wartość javascriptową, nawet obiekty, tablice, funkcje czy sam kod JSX!

import { getImageUrl } from './utils.js'

export default function Profile() {
  return (
    <Card>
      <Avatar
        size={100}
        person={{
          name: 'Katsuko Saruhashi',
          imageId: 'YfeOqp2'
        }}
      />
    </Card>
  );
}

function Avatar({ person, size }) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

Chcesz zgłębić ten temat?

Aby dowiedzieć się, jak przekazywać i odczytywać właściwości, przeczytaj rozdział pt. Przekazywanie wartości do komponentu.

Czytaj dalej

Renderowanie warunkowe

Twoje komponenty często będą musiały wyświetlać różne rzeczy w zależności od okoliczności. W Reakcie możesz wyrenderować kod JSX-owy warunkowo używając składni javascriptowej: instrukcji if oraz operatorów && i ? :.

W tym przykładzie użyliśmy operatora && do warunkowego wyrenderowania tzw. “ptaszka”:

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✔'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Lista rzeczy do spakowania Sally Ride</h1>
      <ul>
        <Item
          isPacked={true}
          name="Skafander kosmiczny"
        />
        <Item
          isPacked={true}
          name="Hełm ze złotym liściem"
        />
        <Item
          isPacked={false}
          name="Zdjęcie Tam"
        />
      </ul>
    </section>
  );
}

Chcesz zgłębić ten temat?

Aby poznać różne sposoby renderowania warunkowego, przeczytaj rozdział pt. Renderowanie warunkowe.

Czytaj dalej

Renderowanie list

Na pewno często zdarzy ci się wyświetlić kilka podobnych komponentów na podstawie jakiejś kolekcji danych. W Reakcie możesz użyć javascriptowych funkcji filter() i map(), aby przekształcić tablicę danych w tablicę komponentów.

Dla każdego elementu tablicy musisz określić key (pol. klucz). Zwykle używa się w tym celu ID pobranego z bazy. Klucze umożliwiają Reactowi śledzenie pozycji każdego z elementów listy w sytuacji, gdy ulega ona zmianie.

import { people } from './data.js';
import { getImageUrl } from './utils.js';

export default function List() {
  const listItems = people.map(person =>
    <li key={person.id}>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession + ' '}
        znany(-a) za {person.accomplishment}
      </p>
    </li>
  );
  return (
    <article>
      <h1>Naukowcy</h1>
      <ul>{listItems}</ul>
    </article>
  );
}

Chcesz zgłębić ten temat?

Aby dowiedzieć się jak wyrenderować listę komponentów i jak wybrać odpowiednie klucze, przeczytaj rozdział pt. Renderowanie list.

Czytaj dalej

Czyste komponenty

W JavaScripcie niektóre funkcje są czyste. Czysta funkcja:

  • Zajmuje się tylko swoimi sprawami. Nie zmienia żadnych obiektów ani zmiennych, które istniały przed jej wywołaniem.
  • To samo wejście, to samo wyjście. Dla takich samych danych wejściowych czysta funkcja zawsze zwraca ten sam wynik.

Pisząc komponenty w formie czystych funkcji możesz ustrzec się przed masą kłopotliwych błędów i nieprzewidywalnych zachowań w razie rozrostu projektu. Poniżej przedstawiliśmy przykład komponentu nieczystego:

let guest = 0;

function Cup() {
  // Źle: zmienia istniejącą wcześniej zmienną!
  guest = guest + 1;
  return <h2>Herbatka dla gościa #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup />
      <Cup />
      <Cup />
    </>
  );
}

Możesz przekształcić ten komponent w czysty przekazując mu właściwość zamiast modyfikować istniejącą już zmienną:

function Cup({ guest }) {
  return <h2>Herbatka dla gościa #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup guest={1} />
      <Cup guest={2} />
      <Cup guest={3} />
    </>
  );
}

Chcesz zgłębić ten temat?

Aby dowiedzieć się jak pisać czyste funkcje o łatwym do przewidzenia wyniku działania, przeczytaj rozdział pt. Czyste komponenty.

Czytaj dalej

Twój UI jako drzewo

React używa drzew do modelowania relacji pomiędzy komponentami i modułami.

Drzewo renderowania w Reakcie to reprezentacja relacji rodzic-dziecko między komponentami.

Graf drzewa z pięcioma węzłami, każdy z nich reprezentuje komponent. Węzeł korzenia znajduje się u góry grafu drzewa i jest oznaczony jako 'Root Component'. Ma on dwie strzałki rozciągające się w dół do dwóch węzłów oznaczonych jako 'Component A' i 'Component C'. Każda z strzałek jest oznaczona jako 'renders'. 'Component A' ma pojedynczą strzałkę 'renders' do węzła oznaczonego jako 'Component B'. 'Component C' ma pojedynczą strzałkę 'renders' do węzła oznaczonego jako 'Component D'.
Graf drzewa z pięcioma węzłami, każdy z nich reprezentuje komponent. Węzeł korzenia znajduje się u góry grafu drzewa i jest oznaczony jako 'Root Component'. Ma on dwie strzałki rozciągające się w dół do dwóch węzłów oznaczonych jako 'Component A' i 'Component C'. Każda z strzałek jest oznaczona jako 'renders'. 'Component A' ma pojedynczą strzałkę 'renders' do węzła oznaczonego jako 'Component B'. 'Component C' ma pojedynczą strzałkę 'renders' do węzła oznaczonego jako 'Component D'.
Przykład drzewa renderowania w Reakcie.

Komponenty blisko góry drzewa, w okolicach głównego komponentu (ang. root component), uważane są za komponenty najwyższego poziomu (ang. top-level components). Komponenty, które nie posiadają komponentów potomnych, są liśćmi (ang. leaf components). Takie kategoryzowanie komponentów jest przydatne do zrozumienia przepływu danych i wydajności renderowania.

Modelowanie relacji między modułami javascriptowymi to kolejny przydatny sposób na zrozumienia jak działa aplikacja. Nazywamy to drzewem zależności modułów.

Graf drzewa z pięcioma węzłami. Każdy węzeł reprezentuje moduł JavaScript. Najwyższy węzeł jest oznaczony jako 'RootModule.js'. Ma trzy strzałki rozciągające się do węzłów: 'ModuleA.js', 'ModuleB.js' i 'ModuleC.js'. Każda strzałka jest oznaczona jako 'imports'. Węzeł 'ModuleC.js' ma pojedynczą strzałkę 'imports', która wskazuje na węzeł oznaczony jako 'ModuleD.js'.
Graf drzewa z pięcioma węzłami. Każdy węzeł reprezentuje moduł JavaScript. Najwyższy węzeł jest oznaczony jako 'RootModule.js'. Ma trzy strzałki rozciągające się do węzłów: 'ModuleA.js', 'ModuleB.js' i 'ModuleC.js'. Każda strzałka jest oznaczona jako 'imports'. Węzeł 'ModuleC.js' ma pojedynczą strzałkę 'imports', która wskazuje na węzeł oznaczony jako 'ModuleD.js'.
Przykład drzewa zależności modułów.

Drzewo zależności jest często wykorzystywane przez narzędzia do budowania w celu bundlowania całego kodu JavaScript, niezbędnego dla klienta do pobrania i renderowania. Duży rozmiar bundla wpływa negatywnie na doświadczenie użytkownika w aplikacjach reactowych. Zrozumienie drzewa zależności modułów jest pomocne przy debugowania tego typu problemów.

Chcesz zgłębić ten temat?

Przeczytaj rozdział Twój UI jako drzewo, aby dowiedzieć się, jak tworzyć drzewa renderowania i zależności modułów dla aplikacji reactowych oraz jak mogą być one przydatne jako modele mentalne używane do poprawy doświadczenia użytkownika i wydajności aplikacji.

Czytaj dalej

Co dalej?

Zacznij od rozdziału pt. Twój pierwszy komponent!

Albo, jeśli znasz już ten temat od podszewki, może chcesz dowiedzieć się, jak dodać interaktywność w aplikacji?