Pierwsze kroki z biblioteką React Javascript

Opublikowany: 2022-02-16

Jeśli spędzasz czas w świecie WordPressa, prawdopodobnie spotkałeś Zaca Gordona. Zac jest entuzjastycznym i uroczym nauczycielem, który koncentruje się na JavaScript w WordPressie. Zapożyczył frazę „Naucz się głęboko Javascript” od Matta Mullenwega i uczynił z niej swój osobisty slogan.

Przez ostatnie kilka lat Zac produkował zajęcia wideo, konferencje online, rozmowy na żywo i podcasty, które skupiają się na nauce korzystania z JavaScript w WordPress.

Z radością mogę powiedzieć, że pracujemy z Zacem nad książką „React Explained”. React to biblioteka, którą zespół WordPress wybrał dla nowego edytora Gutenberga. Nie są sami – zarówno Drupal, jak i Magento również wybrali Reacta.

Zac tweetuje na żywo swoje postępy w pisaniu na @zgordon na Twitterze. Możesz także usłyszeć, jak Zac opowiada o React w podcaście OSTraining.

W tym fragmencie z książki Zac przedstawia ogólny przegląd Reacta i kilka kluczowych pojęć.


Czym jest React?

Po wyjęciu z pudełka React jest biblioteką do budowania interfejsów użytkownika.

Chociaż React jest biblioteką JavaScript, interfejsy, które buduje React, są niezależne.

Istnieje kilka bibliotek towarzyszących React, dzięki którym tworzone interfejsy działają w przeglądarce, na serwerze, w natywnych aplikacjach, a nawet w środowiskach 360 i VR. W tej książce skupiamy się na pracy z ReactDOM, biblioteką zarządzającą dodawaniem interfejsów, które tworzymy za pomocą Reacta do stron internetowych i aplikacji po stronie klienta. ReactDomServer, ReactNative i React360 to także biblioteki, które możesz chcieć zbadać, aby używać interfejsów React w innych środowiskach.

Oprócz zapewniania funkcji pomocniczych do budowania interfejsów, architektura Reacta umożliwia obsługę interakcji z interfejsami, niezależnie od tego, czy dotyczy to obsługi zdarzeń, wywołań API, zarządzania stanami, aktualizacji interfejsu czy bardziej złożonych interakcji.

React nie zapewnia tylu funkcji pomocniczych, co niektóre frameworki JavaScript. To w dużej mierze dlatego nazywamy React biblioteką, a nie frameworkiem. Nadal będziesz musiał pisać dużo waniliowego JavaScript podczas pracy z Reactem.


Wyjaśnienie architektury komponentów React

Komponent to niezależny fragment kodu wielokrotnego użytku (utworzony za pomocą funkcji lub klasy).

React wykorzystuje architekturę komponentów do budowania interfejsów użytkownika i organizowania kodu. Główny plik prostej aplikacji React może wyglądać mniej więcej tak.

 // Importuj React i inne komponenty
importuj React z „react”;
importuj { render } z 'react-dom';
importuj nagłówek z './Header';
importuj treść główną z „./treść główna”;
importuj stopkę z './Footer';

funkcja App(){
  powrót (
    <div className="aplikacja">
      <Nagłówek />
      <Treść główna />
      <Stopka />
    </div>
 );
}

ReactDOM.render( <App />, document.getElementById("root"));

Widzimy tutaj kilka używanych komponentów. <Header />, <MainContent /> i <Footer /> to wszystkie komponenty. Funkcja App() jest również komponentem i widzimy w ostatnim wierszu tego przykładu, jak możemy wykorzystać bibliotekę ReactDOM i metodę ReactDOM.render() do zarządzania dodawaniem interfejsu użytkownika, który zbudowaliśmy do strony internetowej.

Jeśli zagłębimy się w komponenty <Header />, <MainContent /> i <Footer />, prawdopodobnie zobaczylibyśmy użycie większej liczby komponentów, a także coś, co wygląda jak znaczniki HTML.

 importuj React z „react”;
importuj ogłoszenie z "../Ogłoszenie";
importuj logo z "../assets/logo.svg";

eksportuj funkcję domyślną Header() {
powrót (
<header className="app-header">
<Ogłoszenie />
<img src={logo} className="app-logo" alt="logo" />
<h1 className="app-title">Nazwa witryny</h1>
</header>
);
}

W powyższym komponencie <Header /> widzimy, że pobieramy kolejny komponent o nazwie <Ad />. Większość aplikacji React zawiera kilka warstw zagnieżdżania komponentów, tak jak w przypadku <App />, <Header /> i <Ad />.

Widzimy również użycie elementów HTML w naszym kodzie React. Jest to możliwe dzięki bibliotece o nazwie JSX, która pozwala na pisanie „znaczników HTML” bezpośrednio w JavaScript. Ponieważ używamy React do tworzenia interfejsów użytkownika, a interfejsy użytkownika w sieci zawierają znaczniki HTML, ma to sens, abyśmy widzieli elementy podobne do HTML w naszych komponentach interfejsu użytkownika. W tej książce szczegółowo omówimy JSX.

Jeśli spojrzymy na kod z prostej aplikacji Reacta zbudowanej przy użyciu React 360, biblioteki VR Reacta, rzeczywiste komponenty, które wywołujemy, będą inne, ale architektura komponentów jest nadal obecna.

 importuj React z „react”;
import {
  Tekst,
  Pogląd,
  Przycisk Vr,
} z 'react-360';

class Slideshow rozszerza React.Component {
  // Kod usunięty dla zwięzłości
  powrót (
    <View style={styles.wrapper}>
      <View style={styles.controls}>
        <VrButton onClick={this.prevPhoto}>
          <Text>{'Poprzedni'}</Text>
        </VrButton>
        <VrButton onClick={to.nextPhoto}>
         <Text>{'Dalej'}</Text>
        </VrButton>
      <Widok>
        <Text style={styles.title}>{current.title}</Text>
      </View>
    </View>
  );
}

Powyższy kod tworzy kilka warstw widoków 360 z nałożonymi przyciskami i tekstem. Chociaż sam kod może nie mieć pełnego sensu, powinno być jasne, że mamy kilka zagnieżdżonych komponentów reprezentujących widok, przyciski i tekst.

Jest to dobry przykład, ponieważ możesz zobaczyć, jak te same komponenty są ponownie wykorzystywane na różne sposoby, przekazując im różne parametry lub to, co React nazywa props. Zrozumienie, w jaki sposób dane przechodzą przez komponenty React, jest ważne dla zrozumienia typowej architektury komponentów używanej do budowania za pomocą Reacta.


Objaśnienie przepływu danych React

React przestrzega konwencji pobierania i ustawiania danych w najwyższym punkcie niezbędnym w hierarchii komponentów, aby dane mogły być przekazywane w jedną stronę w dół przez aplikację. Przyjrzyjmy się temu przykładowi i wyobraźmy sobie niektóre typy danych, których potrzebowalibyśmy dla różnych komponentów.

 funkcja App() {
  powrót(
    <Fragment.Reaguj>
      <Nagłówek />
      <Treść />
      <Pasek boczny />
      <Stopka />
    </React.Fragment>
  );
}

Może być konieczne udostępnienie czegoś takiego jak nazwa witryny zarówno dla <Header />, jak i <Footer />. Główna treść konkretnej strony musiałaby zostać przekazana do <Content />. Niektóre dodatkowe dane widżetów mogą wymagać przejścia do <Pasek boczny />.

 funkcja App() {
  const tytuł_strony = pobierz tytuł_strony();
  const widgets = getWidgets();
  const mainContent = getPageContent();
  powrót(
    <Fragment.Reaguj>
      <Nagłówek tytuł_strony={tytuł_strony} />
      <Content mainContent={mainContent} />
      <Widżety paska bocznego={widgety} />
      < Stopka tytuł_strony={tytuł_strony} />
    </React.Fragment>
  );
}

Ta konwencja tworzenia nazw atrybutów i przypisywania im wartości jest sposobem, w jaki przekazujemy dane do komponentu.

Teraz <Header /> i <Footer /> mają dostęp do tytułu strony, <Content /> ma dostęp do głównej zawartości, a <Sidebar /> ma dostęp do widżetów, których potrzebuje.

Ważną uwagą jest to, że ten wzorzec przekazywania danych do komponentu przekazuje dane tylko na jednym poziomie. Komponenty wewnątrz <Header /> nie uzyskają automatycznie dostępu do siteTitle.

 function Nagłówek(rekwizyty) {
  powrót(
    <nagłówek>
      <p>Możemy zobaczyć {props.siteTitle} tutaj.</p>
      <PageHeader siteTitle={props.siteTitle} />
      <PageSubHeader />
    </header>
  );
}

Widać tutaj, że wewnątrz <Header /> możemy wywołać props.siteTitle i mieć dostęp do przekazanej do niego wartości. Jeśli jednak chcielibyśmy mieć dostęp do siteTitle w komponencie <PageHeader />, musielibyśmy również ręcznie przekazać te informacje.

Gdy komponent otrzymuje wartość jako właściwość, nie powinien jej modyfikować. Rekwizyty powinny przechodzić przez drzewo komponentów jako niezmienne dane. Gwarantuje to, że każdy komponent, który odwołuje się do właściwości, odwołuje się do tej samej wartości, co jego rodzic komponentów potomnych.

Wartość właściwości powinna być zmieniana tylko w komponencie, który pierwotnie ustawił wartość właściwości i zaczął przekazywać ją przez drzewo komponentów. W powyższym kodzie przykładowym komponent <App /> może zmienić wartość siteTitle, ale komponenty <Header /> lub <PageHeader /> nie powinny.

Aby zrozumieć przepływ, w jaki sposób dynamiczne dane są aktualizowane w aplikacji React, należy omówić stan i sposób, w jaki procedury obsługi zdarzeń mogą być przekazywane jako właściwości.


Wyjaśnienie stanów komponentów React

Jak się dowiedzieliśmy, dane przepływają w postaci niezmienionej przez komponenty jako rekwizyty. Dane są ustawiane na najwyższym komponencie w drzewie niezbędnym do przekazania wszystkim komponentom potomnym informacji potrzebnych jako właściwości.

W niektórych przypadkach dane te są odbierane raz i nie trzeba ich zmieniać. Jednak w wielu przypadkach dane te muszą pozostać dynamiczne i mieć możliwość aktualizacji w dowolnym momencie, a aktualizacja powinna być odzwierciedlona we wszystkich komponentach podrzędnych.

Aby śledzić dane, które zmieniają się w React, mamy obiekt stanu React i zestaw funkcji pomocniczych do aktualizacji wartości stanu.

Oto przykład licznika, który sam się zaktualizuje. Wartość licznika jest wartością dynamiczną w tym komponencie i dlatego stanowi dobry przykład, kiedy można polegać na stanie. Zauważ, że aby tworzyć komponenty ze stanem, musimy użyć klas JavaScript, a nie funkcji.

 klasa Licznik rozszerza Składnik {
  stan= {
    licznik:0
  };

  Liczba obsługi = () => {
    this.setState({
      licznik: ten.stan.licznik + 1
    });
  };

  renderowanie() {
    powrót (
      <div>
        <h1>{this.state.counter}</h1>
        <button onClick={this.handleCount}>Odliczaj!!</button>
      </div>
    );
  }
}

Teraz należy zauważyć, że ten stan obejmuje tylko ten składnik. Wartość stanu w liczniku nie byłaby dostępna dla komponentów podrzędnych lub nadrzędnych.

Tak więc w bardziej złożonym przykładzie, jak poniżej, musielibyśmy przekazać wartość counter w dół jako właściwość do elementu potomnego.

 klasa Licznik rozszerza Składnik {
  stan= {
    liczba: 0
  };

  Liczba obsługi = () => {
    this.setState({
      liczba: this.state.count + 1
    });
  };

  renderowanie() {
    powrót (
      <div>
        <PageHeader count={this.state.count} />
        <button onClick={this.handleCount}>Odliczaj!!</button>
      </div>
    );
  }
}

Właściwość licznika <PageHeader /> jest aktualizowana za każdym razem, gdy aktualizujemy stan w komponencie <Counter />

 function nagłówek strony(rekwizyty) {
  zwróć <h1>{props.count}</h1>; 
}

Fajną rzeczą w tym podejściu jest to, że za każdym razem, gdy stan jest aktualizowany, nowa wartość zostanie automatycznie przekazana do wszystkich komponentów potomnych z wartością właściwości ustawionej na stan.

Dzięki temu mamy jeden punkt prawdy dla danych dynamicznych. Źródłem prawdy jest wartość w stanie, zarządzana z jednego elementu. Wszystkie wystąpienia tej wartości w komponentach potomnych są wartościami niezmiennymi otrzymanymi jako właściwości, których nie należy zmieniać poza tym komponentem.

Komponenty, które pojawiają się nad tym komponentem w hierarchii nie miałyby dostępu do tych danych, ponieważ są one przekazywane tylko za pośrednictwem rekwizytów. Ponownie widzimy, dlaczego próbujemy ustawiać i zarządzać stanem z komponentów znajdujących się wyżej w hierarchii, aby dane były dostępne dla wszystkiego, co ich potrzebuje.

Istnieje kilka innych wzorców architektury, takich jak komponenty wyższego rzędu i interfejs API kontekstu, które omijają konieczność ręcznego przekazywania ton rekwizytów przez aplikację. Omówimy je bardziej szczegółowo później. Na razie chcemy się upewnić, że rozumiemy ten ogólny przegląd ogólnych zasad działania, zanim zaczniemy iść na skróty.

Aktualizowanie stanów komponentów w React

A teraz, co się dzieje, gdy chcemy wywołać aktualizację stanu z komponentu potomnego?

Wyobraź sobie na przykład, że w powyższym przykładzie chcielibyśmy mieć komponent <Button /> zamiast zakodowanego na stałe przycisku w naszym głównym komponencie <Counter />? W rzeczywistości jest to dość powszechne w złożonych aplikacjach.

Rozwiązaniem tego problemu w świecie Reacta jest przekazanie funkcji obsługi zdarzeń, która aktualizuje stan za pomocą setState jako właściwości. Następnie można go wywołać z dowolnego komponentu podrzędnego, ale akcja będzie miała miejsce w oryginalnym komponencie, który ustawia stan i ma również możliwość jego aktualizacji. Istnieją inne wzorce, ale to podejście jest najbardziej podstawowe.

Jeśli nie jesteś zaznajomiony z przekazywaniem funkcji jako parametrów, jest to całkowicie prawidłowy JavaScript.

Gdy stan zostanie zaktualizowany, zostanie on przekazany przez hierarchię komponentów za pośrednictwem właściwości.

Oto przykład tego, jak by to wyglądało.

 klasa Licznik rozszerza Składnik {
  stan= {
    liczba: 0
  };

  Liczba obsługi = () => {
    this.setState({
      liczba: this.state.count + 1
    });
  };

  renderowanie() {
    powrót (
      <div>
        <PageHeader count={this.state.count} />
        <Button handleCount={this.handleCount} />        
      </div>
    );
  }
}

function PageHeader( rekwizyty ) { 
  powrót( 
    <h1>{props.count}</h1>
 ); 
}

przycisk funkcyjny ( rekwizyty ) {
  powrót(
    <button onClick={props.handleCount}>Odliczaj!!</button>  
  );
}

Tutaj widzimy prosty przykład tego, jak React obsługuje przepływ danych. Dla danych istnieje jeden punkt prawdy. Który istnieje w zestawie stanów i jest aktualizowany z jednego komponentu. Dane są przekazywane w jedną stronę w dół przez zagnieżdżone drzewo komponentów za pomocą rekwizytów.

Jeśli stan musi zostać zaktualizowany z komponentu innego niż ten, w którym został pierwotnie ustawiony, procedura obsługi zdarzeń może zostać przekazana do niezbędnego komponentu podrzędnego jako właściwość. Dzięki temu dane są niezmienne i przepływają w jedną stronę, ponieważ nawet jeśli komponent podrzędny wyzwoli zmianę, zmiana ta ma miejsce wyżej w oryginalnym komponencie.

Kiedy przypisujemy wartość właściwości do czegoś ze stanu, jak poniżej, ta wartość właściwości zostanie automatycznie zaktualizowana przy każdej zmianie stanu.

 <PageHeader counter={this.state.count} />

Każdy inny komponent podrzędny, który odwołuje się do tej wartości właściwości, również automatycznie otrzyma aktualizację. Na tym polega piękno przepływu danych w React.

Przyzwyczajenie się do tego może trochę potrwać, w zależności od tego, jak w przeszłości podchodziłeś do takich problemów z JavaScriptem. Wszystko to powinno jednak służyć jako dobry punkt wyjścia, abyśmy mogli głębiej zagłębić się w wyjaśnianie Reacta.