W poprzednim wpisie przyjrzeliśmy się jak obsługiwać zdarzenia z poziomu React, a w tym wykorzystamy tę wiedzę, by rozszerzyć funkcjonalność naszej strony i dowiemy się czym jest bardzo ważna rzecz – stan komponentu.
Nowy komponent – Showcase
Zacznijmy od stworzenia nowego komponentu o nazwie Showcase, w którym będziemy wyświetlać w większym rozmiarze obraz, na który kliknie użytkownik.
Pozostałe części cyklu:
- Utworzenie projektu i pierwszy komponent
- Komponenty i props
- Obsługa zdarzeń
- Stan komponentu i przepływ danych
- Dynamiczne tworzenie komponentów, key i forumlarz
- Hook useEffect
- Zapisywanie danych w chmurze – Firebase
- Wdrożenie aplikacji na zewnętrznym serwerze – Heroku
Tutorial React w formie wideo dostępny jest na kursy.clockworkjava.pl
Zacznijmy od wersji ze statycznym adresem grafiki.
export const Showcase = () => { return ( <img src="https://images.unsplash.com/photo-1580109672851-b85640868813" height="525" width="600" ></img> ); }; export const App = () => { return ( <div onClick={event => console.log("Event from div")}> <Image url="https://images.unsplash.com/photo-1508138221679-760a23a2285b" /> <Image url="https://images.unsplash.com/photo-1474487548417-781cb71495f3" /> <Image url="https://images.unsplash.com/photo-1580109672851-b85640868813" /> <Image url="https://images.unsplash.com/photo-1580046939256-c377c5b099f1" /> <Image url="https://images.unsplash.com/photo-1576801488695-2e4d7a14b8b5" /> <Showcase /> </div> ); };
Wszystko fajnie, jednak chcemy, by adres URL się zmieniał w zależności od klikniętego obrazu, więc zmieniał się.
Tak przy okazji… obrazki będą ładować się wolno, bo są w gigantycznej rozdzielczości 🙂
Stan komponentu
Stan jest spokrewniony z propsami. Jest to czysto Reactowa rzecz. O ile propsy przychodzą z góry jako właściwości komponentu przy jego tworzeniu, to state może się zmieniać w zależności od tego, jakie akcje wykona użytkownik. Dodatkowo każda zmiana stanu powoduję ponowne wyrysowanie komponentu w oknie przeglądarki, czyli pozwala na zmianę tego, co widzi użytkownik bez konieczności przeładowania strony.
Stan w komponentach funkcyjnych (a na nich się w tym tutorialu koncentrujemy) tworzymy za pomocą metody useState.
Jest to jeden z podstawowych hooków. Tak zwane hooki w React to specjalne funkcje, które zostały dodane w wersji 16 tej biblioteki. Dzięki nim komponenty funkcyjne zyskały o wiele więcej możliwości, bo wcześniej stan i ogrom innych możliwości był zarezerwowany tylko dla komponentów klasowych, które wymagały więcej kodu do utworzenia.
useState jako argument przyjmuje domyślną (pierwszą) wartość, jaką dany stan ma przyjąć, a zwraca dwie rzeczy: zmienną, pod którą dany stan będzie dostępny, oraz funkcję, za pomocą której będziemy mogli wartość stanu zmienić.
import React, { useState } from "react"; import ReactDOM from "react-dom"; //... export const Showcase = () => { const [url, setUrl] = useState( "https://images.unsplash.com/photo-1580109672851-b85640868813" ); return <img src={url} height="525" width="600"></img>; };
Dodaliśmy import, by móc korzystać z funkcji useState, a następnie użyliśmy jej, by utworzyć nowy stan – url. Dodatkowo dostajemy odnośnik do kolejnej funkcji – setUrl, która będzie wykorzystywana do ustawienia stanu.
Rozbudujmy jeszcze trochę nasz komponent Showcase, dodajmy użycie metody setUrl
export const Showcase = () => { const [url, setUrl] = useState( "https://images.unsplash.com/photo-1580109672851-b85640868813" ); return ( <> <img src={url} height="525" width="600"></img> <button onClick={e => setUrl("https://images.unsplash.com/photo-1580046939256-c377c5b099f1") } > First </button> <button onClick={e => setUrl("https://images.unsplash.com/photo-1508138221679-760a23a2285b") } > Second </button> </> ); };
Dodaliśmy dwa guziki. Po kliknięciu zmienia się obraz wyświetlany przez komponent Showcase. Bez przeładowania strony. Wszystko dzięki użyciu metody setUrl, która zmienia stan, co powoduje przerysowanie strony, a w efekcie wyświetlenie innego obrazu.
Wszystko fajnie, ale nas interesuje, by użytkownik kliknął komponent Image, a w efekcie komponent Showcase wyświetlił odpowiedni obraz. Nie interesują nas dodatkowe guziki.
Przepływ danych w React
Co możemy zrobić to wychwycić zdarzenie kliknięcia komponent Image, dobrać się do wartości właściwości url i przesłać ją do komponentu Showcase. O ile pierwszą część jesteśmy w stanie już zrobić, to przesłanie danych pomiędzy komponentami nie jest na pierwszy rzut oka takie oczywiste (choć na drugi jest już bardzo fajnym i „czystym” rozwiązaniem).
Obecnie drzewo komponentów aplikacji wygląda tak:
Mamy komponent App, który zawiera pewną ilość komponentów Image oraz jeden komponent Showcase.
Chcemy przesłać dane z komponentu Image do komponentu Showcase. W obecnej sytuacji jest to jednak w React niemożliwe. Dlaczego?
W React dane możemy przesyłać tylko w dół drzewa DOM, czyli do komponentów, które nasz komponent zawiera, są jego bezpośrednimi potomkami. Robimy to za pomocą props. Nie ma możliwości przesyłania ani w górę drzewa, do rodzica, ani do swoich braci i sióstr.
W React dane przesyłane są tylko w dół drzewa komponentów.
Podejście do rozwiązania naszego problemu jest następujące: dane, które chcemy współdzielić pomiędzy rodzeństwem ustawiamy jako stan ich rodzica, a następnie przesyłamy do potomstwa ów stan i, jeśli jest taka potrzeba, funkcję do jego zmiany.
Wracając na grunt naszej galerii. Chcemy współdzielić (ustawiać w jednym komponencie, a czytać w drugim) adres URL obrazu, który ma być wyświetlany w Showcase. Ustawmy więc ten stan na poziomie komponentu-rodzica, czyli komponentu App.
export const App = () => { const [showcaseURL, setShowcaseURL] = useState(""); return ( <div> <Image url="https://images.unsplash.com/photo-1508138221679-760a23a2285b" /> <Image url="https://images.unsplash.com/photo-1474487548417-781cb71495f3" /> <Image url="https://images.unsplash.com/photo-1580109672851-b85640868813" /> <Image url="https://images.unsplash.com/photo-1580046939256-c377c5b099f1" /> <Image url="https://images.unsplash.com/photo-1576801488695-2e4d7a14b8b5" /> <Showcase /> </div> ); };
Następnie chcemy by stan ten ustawiany był po kliknięciu komponent Image. Propsy mają tę zaletę, że możemy tam wysyłać nie tylko wartości prymitywne czy obiekty, ale też i funkcje. Możemy więc do komponentu Image wysłać funkcję służącą do ustawiania stanu showcaseURL i wywołać ją po kliknięciu w tag img.
export const Image = ({ url, setShowcaseURL }) => { const handleOnClick = event => { event.preventDefault(); event.stopPropagation(); setShowcaseURL(url); }; return <img src={url} height="175" width="200" onClick={handleOnClick}></img>; }; export const App = () => { const [showcaseURL, setShowcaseURL] = useState(""); return ( <div> <Image url="https://images.unsplash.com/photo-1508138221679-760a23a2285b" setShowcaseURL={setShowcaseURL} /> <Image url="https://images.unsplash.com/photo-1474487548417-781cb71495f3" setShowcaseURL={setShowcaseURL} /> <Image url="https://images.unsplash.com/photo-1580109672851-b85640868813" setShowcaseURL={setShowcaseURL} /> <Image url="https://images.unsplash.com/photo-1580046939256-c377c5b099f1" setShowcaseURL={setShowcaseURL} /> <Image url="https://images.unsplash.com/photo-1576801488695-2e4d7a14b8b5" setShowcaseURL={setShowcaseURL} /> <Showcase /> </div> ); };
Dodatkowo w komponencie App, przy tworzeniu komponentów Image dodajemy propsa setShowcaseURL i jako jego wartość podajemy funkcję do zmiany stanu, którą zwrócił nam useState.
Pomimo, że funkcja zostanie wywołana w innym komponencie, niż została zdefiniowana, to wciąż samo wywołanie będzie miało miejsce w kontekście komponentu App i zmieni nam jego stan.
Ostatnią rzeczą do zrobienia jest przesłanie url klikniętego obrazu do komponentu Showcase jako props. Przy okazji wywalimy zbędne buttony.
export const Showcase = ({ url }) => { return ( <> <img src={url} height="525" width="600"></img> </> ); };
W komponencie App przesyłamy wartość stanu showcaseURL jako props url.
export const App = () => { const [showcaseURL, setShowcaseURL] = useState(""); return ( <div> <Image url="https://images.unsplash.com/photo-1508138221679-760a23a2285b" setShowcaseURL={setShowcaseURL} /> //... <Showcase url={showcaseURL} /> </div> ); };
Gdy teraz odpalimy naszą aplikację za pomocą npx parcel ./src/index.html w końcu będzie ona działać jak należy
Podsumowywując:
- Stan komponentu to wartość wewnętrzna danego komponentu, której to zmiana powoduje przerysowanie komponentu.
- Do utworzenia stanu i funkcji go zmieniającej używamy metody useState z biblioteki react.
- W React dane mogą być przesyłane do innych komponentów tylko w dół drzewa DOM, za pomocą props.
- Jeśli jakieś komponenty muszą współdzielić stan, wówczas przenosi sie go do wspólnego komponentu nadrzędnego.
- Jako props możemy przesyłać funkcję, a szczególne funkcje do ustawiania stanu
3 komentarze do “Wprowadzenie React #4 (2020) – stan komponentu i przepływ danych”