03 React

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 56

INŻYNIERIA INTERNETU

WYKŁADY INFORMATYKA TECHNICZNA


2023/2024

REACT

Krzysztof Bzowski <kbzowski@agh.edu.pl>


DOM
• DOM (Document Object Model)
to struktura drzewiasta, która
reprezentuje elementy strony
internetowej w przeglądarce
internetowej. Jest to
abstrakcyjny interfejs
programowania, który umożliwia
programistom manipulowanie
zawartością strony internetowej
i jej wyglądem za pomocą kodu
JavaScript.
SPA – SINGLE PAGE APPLICATION
• MPA (Multiple Page Application) to klasyczne rozwiązanie, w którym
dynamiczna treść HTML jest generowana po stronie serwerowej.
• SPA (Single Page Application) – aplikacja renderowana jest w
przeglądarce. Dzięki temu kolejne akcje nawigacji odbywają się bez
przeładowania całej strony, a jedynie części drzewa DOM. Nie oznacza
to, że aplikacja zbudowana jest z jednej strony, ale że jej zawartość
dynamicznie może się zmieniać. Technicznie, jest to jeden plik HTML
w którym umieszczamy i podmieniamy komponenty. Zwykle* cała
aplikacja jest ładowana na początku i tylko dynamicznie
aktualizowana, gdy użytkownik interaktywnie działa z aplikacją.

*Istnieją metody tzw. dynamicznego importu komponentów (tzw. Lazy Loading) pozwalający na Code Splitting.
Kod komponentów ładowany jest na życzenie
STRATEGIE RENDEROWANIA STRON WWW
1.Static Site Generation (SSG) – Gatsby, Astro
1. Strony są generowane w czasie budowy aplikacji i serwowane jako gotowe pliki
HTML.
2. Zawartość jest statyczna i taka sama dla wszystkich użytkowników.
3. Brak interakcji z serwerem (np. bazą danych), bardzo szybkie renderowanie
strony
4. Bardzo dobra optymalizacja dla silników wyszukiwania (SEO)
2.Client-Side Rendering (CSR) – React
1. Zawartość strony jest renderowana w przeglądarce użytkownika przy użyciu
JavaScript.
2. Strona, którą otrzymuje użytkownik, jest początkowo pusta lub zawiera tylko
minimalny szkielet, a cała zawartość ładuje się i renderuje po stronie klienta.
3. Prędkość renderowania zależy od klienta, słaba optymalizacja SEO – robot
indeksujący musi „uruchomić” JavaScript
3.Server-Side Rendering (SSR) – NextJS
1. Strony są renderowane na serwerze dla każdego żądania
2. Użytkownik otrzymuje kompletnie wyrenderowaną stronę, co jest korzystne
dla SEO i początkowej wydajności ładowania.
3. Możliwość buforowania stron - Incremental Static Regeneration (ISR)
WEBPACK – SERWER DEWELOPERSKI
• Serwer deweloperski (debugowanie i odświeżanie)
• Służy łączeniu kodu (ang. Bundle) i usuwaniu nieużywanego kodu
(ang. tree-shaking)
• Integruje narzędzia transpilujące kod (np. Babel)
• Konfigurowalny za pomocą webpack.config.js
REACT
React to biblioteka JavaScript do
tworzenia interfejsów
użytkownika, która została
stworzona przez Facebook (Meta).
Jest ona wykorzystywana do
budowania aplikacji, w których
potrzebna jest szeroka interakcja z
użytkownikiem.
REACT CECHY
• Komponentowy – React jest oparty na komponentach, co oznacza, że
aplikacja składa się z małych, izolowanych części, które można łatwo
testować i ponownie używać.
• Działa na wirtualnym drzewie DOM (vDOM) - aktualizując tylko te
fragmenty DOM które uległy zmianie. Aktualizacą DOM zajmuje się
algorytm Uzgodnienia (ang. Reconciliation) stanowiący sedno
działania React
REACT CECHY - KOD DEKLARATYWNY A IMPERATYWNY
• Paradygmat imperatywny - skupia się na opisie krok po kroku, jak coś ma być wykonane.

• Kod deklaratywny - mówimy komputerowi co ma dla nas zrobić, opisujemy wynik który chcemy
otrzymać. Pozwala skupić się na wyniku a nie na sposobie w jaki rozwiązujemy problem.
KOMPONENT
Komponent może być funkcją lub klasą. Reprezentuje pewien fragment
aplikacji

Komponent oparty o funkcję Komponent oparty o klasę (przestarzały)

Dalsza część wykładu będzie zakładała pracę


na komponentach funkcyjnych!
Obecnie niezalecane podejście!
JSX, to składnia rozszerzająca język JavaScript, która
KOMPONENT pozwala na pisanie elementów interfejsu
użytkownika w sposób podobny do HTML, ale
bezpośrednio w pliku JavaScript/TypeScript.
• Komponent musi zwracać JSX lub null JSX tworzy wizualną reprezentację komponentu.

To nie jest łańcuch znaków (string) ani HTML

Transpilator

Przeglądarka
KOMPONENT W TYPESCRIPT
Parametry komponentu mogą być
zarówno opisane typem jak i
interfejsem Komponent musi mieć nazwę pisaną wielką literą (to go odróżnia)

Komponent może przyjmować parametry


(ang. properties) w skrócie props.
Jest to zawsze jeden obiekt będący
parametrem funkcji

Zwrócenie wielu linijek JSX wymaga dodania nawiasów!

Jeśli w JSX chcemy osadzić (wyświetlić) dane


muszą być one w klamerkach!
KOMPONENT W TYPESCRIPT
Proces wyświetlenia komponentu na interfejsie
użytkownika (UI) nazywamy renderowaniem.

Nazwa funkcji – nazwa komponentu -> nazwa tagu JSX


Parametry funkcji – parametry komponentu -> atrybuty tagu
JSX
KOMPONENT W TYPESCRIPT
Komponenty można deklarować na wiele sposobów (wszystkie są
równorzędne!)

Destrukturyzacja bezpośrednio w miejscu deklaracji Komponent zadeklarowany jako funkcja strzałkowa


parametrów
KOMPONENT W TYPESCRIPT
Komponenty można deklarować na wiele sposobów (wszystkie są
równorzędne!)

Destrukturyzacja bezpośrednio w miejscu deklaracji parametrów w funkcji


strzałkowej z jawnym zadeklarowaniem typu zwracanego (FC – Functional
Component)
CYKL ŻYCIA KOMPONENTU
• Montowanie („Narodziny” komponentu)
komponent jest tworzony i po raz pierwszy wstawiany do drzewa
DOM
• Aktualizacja („Życie” komponentu)
Komponent może być wielokrotnie aktualizowany poprzez zmianę
parametrów i/lub swojego stanu
• Odłączanie („Śmierć” komponentu)
Gdy komponentu już nie potrzebujemy (na przykład zmieniamy stronę
lub usuwamy część interfejsu), komponent jest odłączany od drzewa
DOM. Stan komponentu jest tracony!
HOOK
• Hook to funkcja specjalna w bibliotece React, która
umożliwia korzystanie z funkcjonalności React w
komponentach funkcyjnych. Hooki zostały
wprowadzone w React 16.8 jako uzupełnienie do
klasowych komponentów.
• Pozwalają one na reużywalność i enkpasulację logiki
między różnymi komponentami. Dzięki temu, że
hooki pozwalają na rozdzielenie logiki od widoku,
możemy łatwo przepisywać i przenosić kod między
różnymi komponentami, co oszczędza czas i
zmniejsza ryzyko błędów.
HOOK
• Hooki w React
• Nazwy hooków muszą zaczynać się od prefiksu "use". Jest to konwencja,
która pozwala na łatwe rozpoznawanie hooków i odróżnianie ich od innych
funkcji.
• Hooki muszą być wywoływane w tych samych miejscach przy każdym
renderowaniu. Nie można ich wywoływać wewnątrz instrukcji warunkowej
lub pętli, ponieważ to może prowadzić do niestabilnego zachowania
komponentu.
• Nie można używać hooków poza komponentami funkcyjnymi lub innymi
hookami.
STAN KOMPONENTU
• Stan komponentu to zbiór danych, które są przechowywane w komponencie i mogą ulegać
zmianom w czasie życia komponentu.
• Komponent jest aktualizowany (renderowany) w DOM TYLKO jeśli jego stan lub parametry ulegną
zmienia.
Zadeklaruj zmienną stanu na początku
komponentu
useState przyjmuje wartość inicjalizująca stan.
Trzeba go sparametryzować, jeśli typ stanu nie
może być wywnioskowany z inicjalizatora!

useState zwraca tablicę: [wartość, setter]


którą destrukturyzujemy (konwencja).
Zawsze przypisuj stan do stałych (const)!

Chcą zmienić stan zawsze użyj setera.


Modyfikacja bezpośrednio stanu nie
aktualizuje komponentu
STAN KOMPONENTU

Aktualizacja wartości zwyczajnej zmiennej nie Aktualizacja wartości stanu bezpośrednio nie
spowoduje aktualizacji komponentu spowoduje aktualizacji komponentu (należy użyć
setera!)

Brak aktualizacji komponentu == brak aktualizacji UI


STAN KOMPONENTU

Stan można sparametryzować typem


jawnym

Jeśli logika zdarzenia jest zbyt


skomplikowana należy ją oddelegować
do dedykowanej funkcji!
W takim przypadku podajemy tylko
nazwę (referencję) do obsługującej
zdarzenie funkcji
TABLICA W STANIE

Zmiana stanu nastąpi kiedy zmianie ulegnie wartość


prymitywu lub adres w pamięci

Atrybut key jest wymagany w dynamicznie tworzonych komponentach Alternatywny zapis


(tj. takich które nie są znane na etapie kompilacji). Musi być UNIKALNY w komponencie!
OBIEKT W STANIE
WIELOKROTNA ZMIANA STANU
Wielokrotna zmiana stanu spowoduje tylko W takich przypadkach stosujemy seter funkcyjny
pojedynczą zmianę. Dlaczego? z odniesieniem do poprzedniej wartości

Użycie takiej konstrukcji zagwarantuje, że każde wywołanie funkcji


aktualizującej otrzyma najbardziej aktualną wartość stanu.
USEREDUCER
Alternatywny sposób zarządzania złożonym stanem, w którym podlega on różnym zmianom (a nie
tylko podmianie)
STAN KOMPONENTU - WSPÓŁDZIELENIE
EFEKT UBOCZNY (ANG. SIDE EFFECT)

Funkcja czysta (ang. pure function) Funkcja z efektami ubocznymi (ang. impure function)

Czysta funkcja to funkcja, która zawsze zwraca to


samo wyjście po podaniu określonego wejścia.
Innymi słowy: działanie jest przewidywalne
Zalety: Testowalność, przewidywalność, łatwość Oprócz zwracania wartości, funkcja wpływa na stan
zrozumienia zewnętrzny lub zależy od jakiegoś stanu zewnętrznego, co
może prowadzić do różnych wyników przy każdym
wywołaniu tej samej funkcji z tymi samymi argumentami.

Efekt uboczny w programowaniu odnosi się do sytuacji, w której funkcja lub wyrażenie modyfikuje stan
zewnętrzny lub ma obserwowalną interakcję z zewnętrznymi stanami poza swoim własnym zakresem. Innymi
słowy, efekt uboczny występuje, gdy operacja powoduje zmiany poza swoim lokalnym środowiskiem, które
mogą wpłynąć na wykonanie innych części programu.
USEEFFECT – OBSŁUGA EFEKTÓW UBOCZNYCH W REACT
• Logika renderująca nie może zawierać efektów ubocznych
• Czym są efekty uboczne? Wszystko co dzieje się „poza” komponentem
• Zdarzenia przeglądarki (np. zmiana wielkości okna)
• Timery (setInterval, setTimeout)
• Czynności które wymagają czyszczenia (np. subskrypcje danych z sieci)
• Coś ma być uruchomione tylko raz przy montowaniu komponentu (np. zapytanie API)
• Reagowanie na zmianę parametrów lub stanu
• Kod umieszczony w useEffect zadziała po każdym renderowaniu lub gdy
zmianie ulegną wartości zadane w zależnościach (dependancies) – drugim
parametrze - useEffect(fun, [deps])
• UseEffect może zwrócić funkcję uruchamianą podczas usuwania
komponentu z DOM (odmontowywania, ang. unmounting)
USEEFFECT useEffect(fun, [deps])

Brak drugiego argumentu


Pusta tablica w drugim parametrze (deps) –
(efekt uruchamiany przy KAŻDYM renderowaniu).
uruchom raz przy pierwszym renderowaniu
Łatwo doprowadzić do nieskończonej pętli!
USEEFFECT

Efekt zostanie uruchomiony zostanie za każdym razem gdy stan count się zmieni,
ponieważ w drugim argumencie mamy count
USEEFFECT – ZMIANA PROPS
Początkowy stan komponentu jest ustawiany
na podstawie propsa initial

Kliknięcie „Naciśnij mnie” nie spowoduje


zmiany stanu w komponencie Counter,
Ponieważ jest on już zainicjalizowany
(inicjalizacja stanu następuje tylko raz)
Wykorzystujemy useEffect do „monitorowania”
parametru initial komponentu.
W przypadku jego zmiany, ustawiamy nowy
stan
USEEFFECT - PORZĄDKI
useEffect NIE musi nic
zwracać. Ale jeśli zostanie
zwrócona funkcja – to
wykona się w momencie
USUNIĘCIA komponentu z
drzewa (ang. umount).

Wykonaj RAZ przy zamontowaniu


(ang. Mount) komponentu

Co się stanie jeśli nie zastopujemy timera?


USEMEMO val = useMemo(fun, [deps])

Zwraca zapamiętaną wartość. Pozwala na „cachowanie” obliczonych


wartości, które byłyby kosztowne w liczeniu za każdym razem gdy
komponent ulega wyrenderowaniu

„Kosztowna obliczeniowo funkcja”

Używać rozważnie,
„cachowanie” nie jest
za darmo

Wartość zwracana będzie


umieszczona w cache, dopóki
wartość parametru w [] się nie
zmieni
USECALLBACK fun = useCallback(fun, [deps])

Jest używany do zapamiętywania referencji do funkcji. Gwarantuje


stabilność wyniku dopóki nie zmieni wartość parametru sterującego.
Tzn. useCallback ZAWSZE zwróci tą samą funkcję przy każdym
renderowaniu dopóki wartość parametru sterującego się nie zmieni.

Zastosowanie: Użyj useMemo do zapamiętywania wyników kosztownych obliczeń, a useCallback do


zapamiętywania funkcji, które przekazujesz jako propsy.

Nie optymalizuj komponentów dopóki nie będzie to potrzebne!


USEREF
Zachowanie referencji do elementów DOM. Pozwala na bezpośrednie
manipulowanie DOM.
USEREF
Zapobieganie niepotrzebnym renderowaniom

useRef jest używany do śledzenia


poprzedniej wartości stanu bez
wywoływania ponownego
renderowania komponentu, co
różni się od użycia useState,
które by wywołało taki ponowny
render. To sprawia, że useRef jest
idealnym narzędziem do
przechowywania wartości, które
mają być zachowane między
renderowaniami, ale których
zmiana nie powinna wywoływać
ponownego renderowania.
FORMULARZE
Zarządzanie formularzami:
• Komponenty kontrolowane (controlled)
Stan formularza jest bezpośrednio zarządzany przez React.
Wartość każdego elementu formularza (jak input, textarea, select)
jest powiązana ze stanem komponentu. Zmiany są śledzone przez
obsługę zdarzeń, takich jak onChange. Ułatwiają walidację danych
formularza i obsługę błędów, ponieważ stan formularza jest zawsze
aktualny i kontrolowany przez React.

• Komponenty niekontrolowane (uncontrolled)


React nie zarządza stanem formularza. Zamiast tego, korzysta się z DOM, aby uzyskać dostęp do
wartości elementów formularza w momencie, gdy formularz jest wysyłany.
Zwykle wymagają mniej kodu niż kontrolowane komponenty, ponieważ nie wymagają obsługi
każdej zmiany stanu. Trudniejsze może być śledzenie bieżącego stanu formularza, co może
utrudniać walidację i obsługę błędów
KOMPONENTY
Blokuje „domyślną”
akcję przeglądarki,
NIEKONTROLOWANE
zapobiegając
przeładowania strony

Formularz jest przetwarzany po


naciśnięciu przycisku z (type=submit)
który wywołuje zdarzenie onSubmit
KOMPONENTY
KONTROLOWANE
Każde pole formularza
odpowiada jednemu polu
w obiekcie stanu.

Każde pole w formularzu


ma podpięte value oraz
obsługę zdarzenia
onChange
OBSŁUGA BŁĘDÓW
W przypadku wystąpienia błędów
ustawiamy stan błędu.

Walidacja danych pozwala na


reagowanie na błędy użytkownika.
Walidacja po stronie przeglądarki
NIGDY nie jest wystarczająca do
zapewnienia poprawności danych!
(Musi być też po stronie serwera)
ZARZĄDZANIE FORMULARZEM
• Zarządzanie danymi – rozbudowany stan, statyczna
kontrola typów.
• Walidacja danych – czy użytkownik wprowadził
prawidłowe informacje oraz informowanie
użytkownika o błędach
• Własna implementacja (czasochłonna)
• Większość bibliotek komponentowych posiada wparcie
do obsługi formularzy
(w tym walidacja danych i obsługa błędów)
• Niezależne implementacje:
• formik (https://formik.org/)
• react-hook-form (https://react-hook-form.com/)
REACT - KOMPONENTY
• Konwencja: Nazwa pliku – nazwa komponentu
• Klamerek używamy do osadzania danych w atrybutach
(paramtetrach) - stringów nie trzeba dawać w klamerkach
• Pewne atrybuty HTML są niedozwolone i mają swoje
zastępstwa (np.class => className), key (zarezerwowany)
• Komponent React zawsze powinien mieć nazwę z wielkiej
litery (małe litery przeznaczone dla natywnego HTML)
• Parametry komponentu zawsze powinny być cameCase
• Parametry zawsze są read-only – nie wolno ich
modyfikować
• Dowolne dane mogą być parametrem
REACT - RENDEROWANIE
• React zaplanuje renderowanie za każdym razem, gdy zmieni się stan
komponentu lub wartości parametrów (props)
• Kiedy komponent rodzic zostanie ponownie wyrenderowany
wszystkie dzieci również zostaną „odświeżone”. Można
zoptymalizować za pomocą React.memo!
REACT.MEMO

Stan komponentu rodzica (App) ulega zmianie == dziecko (Greeting)


ponownie się renderuje, choć jego parametry się nie zmieniły.
REACT.MEMO

Stan komponentu rodzica (App) ulega zmianie == dziecko (Greeting)


ponownie się wyrenderuje tylko gdy parametr name ulegnie zmianie.
Wartość komponentu jest memoryzowana (zapamiętana) dzięki
React.memo.
REACT - KOMPONENTY
• W komponencie możemy używać dowolnych tagów HTML oraz innych
komponentów React – ale możemy zwrócić tylko jeden element
nadrzędny (rodzic). Można użyć pseudo rodzica
<Ract.Fragment/> lub <></> do opakowania kodu jeśli istnieje
konieczność zwrócenia więcej (i nie chcemy wprost stosować innego
tagu)
RENDEROWANIE WARUNKOWE
2
1

3
ERRORBOUNDARIES (GRANICE BŁĘDÓW)
• Przechwytywanie i obsługę błędów w komponentach

Komponent (jak każda funkcja) może W takim przypadku cała aplikacja się
rzucić wyjątek. zawiesi.
Co wtedy?
ERRORBOUNDARIES (GRANICE BŁĘDÓW)
• Biała strona śmierci
(ang. white screen of death)
• F12 lub narzędzia programistów
• Zakładka: Konsola
ERRORBOUNDARIES - ROZWIĄZANIE

npm i react-error-boundary

Granice błędów zatrzymują propagację błędów w drzewie komponentów


i pozwalają na ich obsługę.

Dla dociekliwych, jak samemu zaimplementować Error Boundary:


https://blog.logrocket.com/react-error-handling-with-react-error-
boundary/
FETCH VS XMLHTTPREQUEST
XMLHttpRequest fetch
const xhr = new XMLHttpRequest(); try {
const response = await fetch("http://localhost:9000/api/books",
xhr.open('GET', 'http://localhost:9000/api/books'); { method: "GET" }
);
xhr.onreadystatechange = () => { const data = await response.json();
if (xhr.readyState !== 4) return; // obsluga danych
if (xhr.status === 200) { } catch (e) {
const data = JSON.parse(xhr.responseText); // obsluga wyjatku komunikacyjnego!
// obsluga danych }
} else {
console.log('HTTP error', xhr.status, xhr.statusText);
}
};

xhr.onerror = () => {
// obsluga wyjatku komunikacyjnego!
};

xhr.send();
FETCH VS XMLHTTPREQUEST
• Przeglądarka implementuje dwa API komunikacyjne
• XMLHttpRequest
• API opracowane w 1999 na potrzeby AJAX (Asynchronous JavaScript and XML)
• Strumienie są cachowane w pamięci
• Wparcie dla postępu pobierania danych
• Wparcie dla Timeout
• Wparcie dla przerwania połączenia
• Fetch
• Natywne API opracowane w 2015
• Wspiera Cache (no-store, force-cache itp.)
• Wpiera no-cors (w ograniczonym stopniu)
• Wpiera streaming danych (możliwość pobrania danych w częściach, przykład dużego pliku csv)
• Dostępny w node (od wersji 18)
• Wyjątek powoduje tylko błąd sieci
• Brak wsparcia dla postępu pobierania danych ani timeout
• Przerwanie połączenia możliwe przy użyciu AbortController

Jeśli nie musisz wspierać BARDZO STARYCH przeglądarek powinieneś użyć fetch
ROZWIĄZANIA OPARTE O BIBLIOTEKI STRON TRZECICH
• Implementacje oparte o XMLHttpRequest
• Axios (https://github.com/axios/axios)
• Implementacje oparte o fetch
• Ky (https://github.com/sindresorhus/ky) – tylko przeglądarka
• Gaxios (https://github.com/googleapis/gaxios)
• Got (https://www.npmjs.com/package/got) – tylko Node
• Undici (https://undici.nodejs.org/#/) – tylko Node

class HTTPError extends Error {} import ky from 'ky';

const response = await fetch('https://example.com', { const json = await ky.post('url', {json: {foo: true}}).json();
method: 'POST',
body: JSON.stringify({ foo: true }),
headers: {
'content-type': 'application/json',
},
});

if (!response.ok) {
throw new HTTPError(`Fetch error: ${response.statusText}`);
}

const json = await response.json();


KOMUNIKACJA Z API
KOMUNIKACJA Z
API

Gwarantuje, że dane zostaną


pobrane przy montowaniu
komponentu

Zapytanie sieciowe może być


inicjowane ręcznie (np. po
kliknięciu)
CUSTOM HOOK Dla zaawansowanych: https://usehooks-ts.com

Hook dedykowany do pobierania danych


Wykorzystanie hooka useFetch w komponencie
(useFetch.ts)

You might also like