Polub bloga na fejsie!

Routing ReactJS – wprowadzenie do react-router

20

UWAGA! Ten wpis dotyczy starej wersji react-router! Od tego czasu sporo się w tej bibliotece zmieniło więc napisałem nowy wpis na temat nowego podejścia!

Dzisiejszym wpisem kontynuuję omawianie najważniejszych zagadnień związanych z ReactJS. To już trzeci wpis na ten temat w tym tygodniu i możesz spodziewać się kolejnych przez następne tygodnie! Mam nadzieję, że Ci to odpowiada? No ale do rzeczy… W dzisiejszym wpisie chciałbym omówić routing ReactJS. Będzie to, jak możesz wyczytać w tytule, wprowadzenie do biblioteki react-router, która jest w zasadzie standardem w tym aspekcie.

Na potrzeby tego wpisu przyjmijmy, że mamy strukturę projektu taką samą jak ta, którą stworzyłem w ramach wpisu przedstawiającego podstawy ReactJS. Jeśli chcesz sam sobie postawić ten projekt, to odsyłam do tamtego artykułu. Na końcu tego artykułu znajdziesz też link to repozytorium GitHub zawierającego pełen kod przykładu przedstawionego w dzisiejszym wpisie. Zachęcam do zapoznania się z nim.

Instalacja

Routing ReactJS z użyciem biblioteki react-router to temat dosyć prosty. Zresztą sam się o tym za chwilę przekonasz. Zanim jednak przejdę do omawiania szczegółów konfiguracji, musimy bibliotekę tę zainstalować w naszym projekcie. Aby to zrobić, wystarczy uruchomić poniższą komendę w konsoli (będąc w głównym katalogu projektu):

Zamiast NPM możesz też równie dobrze użyć Yarn:

Zarówno jedna jak i druga z tych komend finalnie spowodują zainstalowanie pakietu react-router jako zależności deweloperskiej. No dobra… skoro niezbędna biblioteka jest już zainstalowana, czas zacząć jej używać!

Konfiguracja routingu

Jak być może pamiętasz z mojego wpisu na temat podstaw ReactJS, każda aplikacja ma dokładnie jeden komponent główny (ang. root component). W naszej przykładowej aplikacji jest to AppComponent znajdujący się w pliku src/components/Main.js. Wprowadzenie routingu do aplikacji powoduje małą zmianę – komponentem głównym będzie teraz komponent Router zawierający konfigurację routingu, natomiast AppComponent stanie się komponentem głównym dla danego „route”.

Na początek spójrzmy na obecną implementację pliku src/index.js, który odpowiada za wyrenderowanie głównego komponentu wewnątrz elementu DOM o indeksie app:

Jak widzisz, komponent zaimportowany z pliku src/components/Main.js jest tym, który jest renderowany jak główny.

No dobrze, dodajmy zatem teraz routing. Aby to zrobić wprowadźmy kilka modyfikacji w powyższym kodzie:

Pierwsze co rzuca się w oczy, to dodatkowy import z pakietu react-router. Importujemy z niego komponenty Router oraz Route, których za chwilę użyjemy do konfiguracji routingu. Oprócz tego importowany jest też obiekt browserHistory ale nie zaprzątajmy nim sobie na razie głowy.

Kolejna sprawa to pierwszy parametr metody render. Jak widzisz, zamiast renderować komponent App, renderujemy komponent Router. Posiada on parametr history, o którym, jak wspomniałem, za moment. To co bardziej teraz interesujące to to, że komponent Router zawiera dziecko: komponent Route. Za jego pomocą definiujemy pojedynczy „route” czyli wiążemy adres (atrybut path, zwróć uwagę, że adres jest względny) z komponentem (atrybut component). W powyższym przykładzie mamy zdefiniowany tylko jeden „route”, odpowiadający ścieżce /. Odpowiada jej komponent App czyli ten, którego implementacja znajduje się w pliku src/components/Main.js.

Atrybut history

Jeśli uruchomiłbyś teraz aplikację przekonałbyś się, że na ten moment działa ona w zasadzie tak samo jak bez skonfigurowanego routingu. Wszystko zmieni się kiedy dodamy do niej kolejne „routy”. Najpierw jednak obiecane wyjaśnienie dla atrybutu history.

Myślę, że dobrze jest to wyjaśnione w dokumentacji biblioteki react-router:

React Router is built with history. In a nutshell, a history knows how to listen to the browser’s address bar for changes and parses the URL into a location object that the router can use to match routes and render the correct set of components.

POLUB BLOGA NA FACEBOOKU!

Chcesz być na bieżąco informowany o nowościach na blogu oraz innych ciekawych treściach? Polub fanpage bloga na Facebooku!

Jak więc widzisz, react-router wykorzystuje bibliotekę history do zarządzania i zapamiętywania adresów widocznych w przeglądarce użytkownika. Generalnie chodzi o to, że przechodząc pomiędzy poszczególnymi „routami” następuje podmiana wyświetlanego komponentu głównego. Cały widok zmienia się bez przeładowania strony w przeglądarce. Dzięki zastosowaniu biblioteki history możliwe jest podmienienie, przez routing ReactJS, adresu w przeglądarce na taki, który odpowiada danemu „route”. Pozwala to również przeglądarce na prawidłowe zapamiętanie odwiedzanych „routów” tak jakby to było osobne strony. To z kolei powoduje, że guzik „Wstecz” przeglądarki działa jak należy – powoduje przejście do poprzednich „routów”.

W ostatnim przykładzie, do atrybutu history komponentu Router przekazaliśmy obiekt browserHistory. W ten sposób można zdefiniować sposób obsługi historii przeglądarki. To nie jedyna dostępna możliwość. Poniżej przedstawiam wszystkie te opcje wraz z wyjaśnieniem:

  • browserHistory – Jest to rekomendowany sposób obsługi historii przeglądarki. Wykorzystuje on API historii wbudowane w przeglądarkę dzięki czemu linki wyglądają standardowo, na przyład: example.com/some/path. Minusem wykorzystania tej metody jest to, że wymaga ona odpowiedniego skonfigurowania serwera. Więcej na ten temat znajdziesz w dokumentacji.
  • hashHistory – Wykorzystuje on znak # w adresie, na przykład: example.com/#/some/path czyli w zasadzie tak jak w starym Angularze. Nie wymaga do działania odpowiedniej konfiguracji serwera, więc jest przydatne głównie do przeprowadzania testów na szybko itp., itd. Adres taki nie wygląda jednak za dobrze, więc dąż do wykorzystania browserHistory,  tak jak radzą autorzy biblioteki.
  • createMemoryHistory – Jak sama nazwa wskazuje, działa w pamięci i nie wykorzystuje ani w żaden sposób nie zmienia adresu przeglądarki. Może być to przydatne w środowiskach innych niż przeglądarka (na przykład kiedy tworzysz projekt React Native).

Dodajemy kolejny route

Ok, skoro podstawową konfigurację mamy już wyjaśnioną, czas rozszerzyć nasz routing ReactJS i dodać kolejny „route”. W tym celu najpierw utwórzmy nowy komponent (znajdziesz go w pliku src/components/About.js):

Jedna uwaga: celowo definiuję ten komponent z użyciem zapisu klasowego a nie funkcyjnego. Komponenty podpinane bezpośrednio pod routing z założenia są zwykle kontenerami, z logiką aplikacji itd. Zapis funkcyjny, tak jak pisałem w poście na temat podziału odpowiedzialności komponentów ReactJS, przeznaczony jest dla bezstanowych komponentów prezentacyjnych.

Skoro mamy już komponent, pora skonfigurować dla niego routing! Po niezbędnych zmianach, kod pliku src/index.js będzie wyglądał następująco:

Na początku oczywiście musimy zaimportować stworzony przed chwilą komponent. To co ciekawe dzieje się wewnątrz komponentu Router. Jak możesz zauważyć, doszedł nam kolejny komponent Route. Wiąże on ścieżkę /about z komponentem About. Jeśli teraz uruchomiłbyś lokalnie swoją aplikację i wpisał w przeglądarce adres: http://localhost:8000/about na ekranie wyświetliłby się tekst „Super Duper Example App!”, tak jak to zdefiniowaliśmy w nowym komponencie.

Nawigacja pomiędzy routami

Jak dotąd myślę, że routing ReactJS może wydawać się bardzo prosty. I tak jest w istocie… Lećmy więc dalej z tym koksem!

Napisałem przed momentem, że aby przetestować nowy „route” należy wpisać jego adres w pasku adresu przeglądarki internetowej. Zamiast tego lepiej jest użyć odpowiednich komponentów nawigacyjnych dostarczanych przez bibliotekę react-router. Zmodyfikujmy co nieco plik src/components/Main.js:

Generalnie wywaliłem trochę niepotrzebnego nam kodu. To co ważne w powyższym przykładzie to import komponentu Link należącego do pakietu react-router. Jego użycie jest bardzo proste, co widać w metodzie render. Wystarczy do atrybutu to przypisać odpowiedni adres „route”.

Dlaczego należy używać komponentu Link?

W zasadzie to powyższy przykład użycia komponentu Link tożsamy jest z użyciem standardowego:

Komponent Link posiada jednak większe możliwości. Jedną z nich jest możliwość zdefiniowania klasy dla aktywnego linku:

Routing ReactJS zawsze wie w jakim jest w danym momencie „route”. Kiedy więc renderowane są komponenty, rozpoznaje on link odpowiadający aktualnemu „route” i może nadać mu odpowiednią klasę. Myślę, że jest to całkiem przydatne dla różnego rodzaju menu nawigacyjnego itp.

Ogólnie rzecz biorąc należy zawsze używać komponentu Link zamiast elementu a ponieważ zapewnia to, że routing ReactJS zadziała poprawnie, a historia przeglądarki zostanie odpowiednio zaktualizowana.

Zagnieżdżanie route

Routing ReactJS pozwala na łatwe zagnieżdżanie kilku „routów”. Jest to przydatne na przykład do tworzenia komponentów layoutu. Zresztą najlepiej będzie pokazać to na przykładzie.

Komponent layoutu

Na początek dodajmy nowy komponent (plik src/components/LayoutWrapper.js):

Jak widzisz, tym razem użyłem notacji funkcyjnej. To dlatego, że ten komponent odpowiada jedynie za zaprezentowanie layoutu strony, razem z menu. Jest on „wrapperem” na pozostałe komponenty.

To co istotne znajduje się wewnątrz elementu div posiadającego klasę container. Użycie {props.children} oznacza, że w tym miejscu renderować się ma komponent – dziecko tego komponentu. Na przykład, jeżeli użyjemy powyższego komponentu w poniższy sposób…

… to dziecko tego komponentu czyli element p zostanie wyrenderowany właśnie w miejscu {props.children}.

Konfiguracja routingu z użyciem layoutu

No ale wracając do tematu – użyjmy komponentu LayoutWrapper w konfiguracji routingu:

W powyższej implementacji pliku src/index.js możesz zauważyć, że konfiguracje „routów” dla strony domowej oraz strony „about” zostały owinięte przez konfigurację „route” dla komponentu LayoutWrapper. Dzięki temu, gdy wejdziesz na stronę / wyświetli Ci się widok komponentu LayoutWrapper, a w miejsce jego {props.children} wstrzyknięty zostanie widok komponentu App. Analogicznie zadzieje się dla „route” komponentu About.

Opisany mechanizm można dowolnie wykorzystywać do wyświetlania odpowiedniej „kaskady” komponentów w zależności od wybranego adresu przeglądarki więc jest to bardzo przydatne narzędzie.

Mała uwaga: z plików src/components/Main.js oraz src/components/About.js można teraz usunąć komponenty Link ponieważ znajdują się już one w LayoutWrapper.

Route indexu

Dla strony indeksu (tej o adresie /) routing ReactJS przewiduje dodatkowy sposób konfiguracji. Nie ma tutaj co się rozwodzić, lepiej spojrzeć na przykład:

Na wstępie importujemy dodatkowy komponent IndexRoute z pakietu react-router. Kolejna sprawa to zmiany w konfiguracji routingu. Zauważ, że definicja ścieżki / przeniesiona została poziom wyżej, natomiast komponent App jest teraz powiązany z komponentem IndexRoute. Z punktu widzenia naszego przykładu nic się nie zmienia i w większości przypadków można tego używać zamiennie. Istnieją szczególne przypadki kiedy ma to znaczenie. Możesz o tym przeczytać w dokumentacji.

Przekazywanie parametrów

Ostatnia sprawa, którą chciałbym poruszyć w ramach tego artykułu to obsługa, przez routing ReactJS, parametrów przekazywanych w adresie. Do zobrazowania tego zagadnienia, dodajmy jeszcze jeden komponent (plik src/components/User.js):

W powyższym przykładzie doszło sprawdzenie typu parametru params. Jest to parametr dodawany do „propsów” przez react-router. Zawiera on parametry przekazane w adresie. Jak widzisz, wykorzystujemy ten parametr do wyświetlenia go na ekranie.

Jak teraz skonfigurować routing? Spójrz na kolejną iterację naszej przykładowej konfiguracji:

Oczywiście doszło nam kolejne użycie komponentu Route. Wiąże on komponent User z adresem /user/:userId. Zapis :userId przekazuje do react-router informację, że w jego miejsce należy spodziewać się parametru, który przekazać należy do komponentu poprzez „propsy”.

Aby to przetestować, wpisz w przeglądarce adres http://localhost:8000/user/1. Na ekranie wyświetli się tekst „User id is: 1”.

Routing ReactJS – podsumowanie

Mam nadzieję, że udało mi się pokazać Ci, że routing ReactJS to nic specjalnie skomplikowanego. Myślę, że to podstawa każdej większej aplikacji z użyciem ReactJS dlatego prędzej czy później będziesz musiał dotknąć tego tematu w swojej pracy. W najbliższym czasie planuję kolejne wpisy na temat „ekosystemu” React tak, aby dopełnić całości jego obrazu jako pełnoprawnego frameworka JavaScript.

Kod przykładu zawartego w artykule

Pełen kod przedstawionych dziś przykładów dostępny jest w repozytorium GitHub. Wystarczy, że sklonujesz repozytorium react-router-example. Po jego sklonowaniu, nie zapomnij uruchomić npm install w celu zainstalowania wszystkich wymaganych zależności!

CHCESZ DARMOWEGO E-BOOKA?

Jeśli chcesz otrzymać mojego e-booka: Rozmowa Kwalifikacyjna - pytania z podstaw JavaScript zostaw mi swój e-mail:

Oprócz tego co poniedziałek dostaniesz maila z listą moich wpisów z poprzedniego tygodnia!

  • Tomasz Trzaskoma

    Hej, dzięki za artykuł i cały cykl o React. Namówiłeś mnie, zdecydowałem się używać react + redux w nowym projekcie 🙂 Znasz może jakieś bibliotekiUI godne polecenia. Na tę chwilę moją uwagę przyciągnęły grommet oraz Material UI. Chociaż to apka głównie desktopowa chciałbym uderzyć jednak w material design.

    • Hej! Cieszę się, że te wpisy są przydatne!
      Co do bibliotek UI to tak na poważnie używałem tylko Bootstrap – generalnie trzeba się zawsze cztery razy zastanowić nad wyborem takiej biblioteki żeby nie okazało się, że to overkill… Na przykład jak wejdziesz właśnie w Bootstrap a używasz z niego tylko grid systemu to to się trochę, moim zdaniem, mija z celem i wtedy lepiej poszukać czegoś lżejszego… Ale jak chcesz material design to faktycznie pewnie lepiej użyć jakiejś biblioteki 😉

  • Mieczyslaw__23

    Już w kolejnym artykule widzę, że przy instalacji paczek robisz to z opcją „–save-dev” (npm install –save-dev react-router) a co wg mnie jest błędem. Z opcją „-dev” powinno się instalować paczki, które są wymagane w procesie developmentu, bez „dev” te które są wymagane do zbudowania projektu. W internetach trochę literatury na ten temat jest, więc nie będę się rozwodził 😉

    Generalnie to fajnie piszesz! Tak trzymaj!

    PS. Pomyślałem kiedyś, że fajnie jakbyś zrobił jakiś prosty projekt stronki SPA (byle nie todo ;-)) i potem przepisywał go z wykorzystaniem innych bibliotek (React, Angular, Polymer, Vue, itd). Taki sugestia jakby Ci brakowało pomysłów na tematy na blogu.

    • co do –save oraz –save-dev nie ma to znaczenia póki nie robisz paczki, którą chcesz opublikować w npm – wtedy było by to istotne – pisałem o tym chyba we wpisie na temat publikacji komponentu React w npm…

      poza tym dzieki za pomysły na wpisy – zawsze sobie takie coś zapisuję na przyszłość 😉

      • Mieczyslaw__23

        Owszem. Chodzi mi o to, że ludzie się uczą od Ciebie i warto pokazywać dobre praktyki od początku. Jestem pewien, że parę osób po prostu Twój kod przekleja bezmyślnie… Potem, kiedyś przejmiesz po swoim „uczniu” kod i się będziesz zastanawiał dlaczego to zostało zrobione tak, a nie inaczej 😉

        Piszę generalnie o wszystkich nawykach (nie tylko „-dev”). Nie chciałbym, żebyś to odebrał jako „czepialstwo”, a raczej taką uwagę, bo Twojego bloga odbieram (nie tylko ja) jako edukacyjnego stąd taka drobna uwaga, żeby od początku uczyć dobrych praktyk.

        • Myślę, że jednak zdecydowanie się czepiasz! 😛
          Tak jak napisałem – nie ma to znaczenia w projektach, które tylko korzystają z pakietów npm – niech sobie ludzie to przeklejają 1:1 i nie zrobią błędu… Poza tym zawsze staram się w przykładach stosować dobre praktyki, jednak nie zawsze każdy uważa coś za dobrą praktykę – zaraz możemy zacząć się spierać czy lepiej używać TAB czy spacji dla tabulatorów i będzie to tak samo bez sensu dyskusja jak ta teraz 😉

  • Okay. To równiez okazało się bardzo prosto napisane.

    oby tak dalej 🙂

  • Dźwiedziu

    Ja tu rozkminiam dlaczego mi na nowym projekcie nie działa, a tu… własnie na dniach wyszła wersja 4.0.0, która wprowadziła spore, niekompatybilne wstecz zmiany. Może zaktualizujesz artykuł? Zmiany są w sumie niewielkie. Masz pull request do swojego przykładu. Przy okazji, w tym pull request jest opcjonalna migracja do Webpack w wersji 2.x 🙂

    • raczej planowałem osobny wpis: „react-router – co nawego w wersji 4″…

      • Dźwiedziu

        Super. A ta wtyczka React Developer Tools pod Chrome Ci działa? Bo mi za chiny nie chce ani pod Windows 10 ani pod Linuksem.

  • Karol Zawada

    Ciekawy artykuł. Jak rozwiązać problem linkowania do element na stronie np po id #cta?

  • Przemek

    Czy Link z react-router nie narzuca nam jakichs styli? Nie potrafie wystylizowac elementow LINK w jednej linii (z bootstrapem) jakkolwiekbym nie probowal..

  • Łukasz Kasprzycki

    Cześć Bartłomiej Dybowski ,nie wiem czy jeszcze odwiedzasz ten artykuł ale mam problem,zaczynam dopiero swoją prace z RJS i próbuje wykonać:
    import { Router, Route, browserHistory } from ‚react-router’
    Ale mój edytor nie importuje browserHistory ;( dodam że pracuje na WebStormie,szukałem rozwiązania w necie ale nie mogę nic sensownego znaleźć.Jeśli masz jakiś pomysł,to byłbym wdzięczny za pomoc 😀

    • cześć to dlatego, że pojawiła się nowa wersja react-routera i jest tam sporo breaking changes – na tę chwilę nie opieraj się na powyższym wpisie tylko na oficjalnej dokumentacji – nowy wpis na ten temat mam właśnie na tapecie i myślę, że pojawi się on w poniedziałek!

      • Łukasz Kasprzycki

        Wow dzięki za niesamowice szybką odpowiedź 😀 na prawdę dzięki bo strasznie się z tym męczę.Gdzieś wyczytałem że react-router 4 jest całkiem inny,dlatego próbowałem zainstalować też starą wersje(ver 2),ale nie pomogło:(
        Także z niecierpliwością czekam na twój wpis 😀

      • przy okazji możesz zrobic porównanie typu react-router v4 vs universal-router 😉

        • o universal-router to raczej osobny wpis, bo wpychanie tutaj jeszcze porównania to się zrobi książka, a nie post na blogu 😉 ale fakt – to też jest temat, który warto poruszyć!

          • tez prawda. generalnie patrze to z pozycji osoby która ma react na tapecie i pracuje w projekcie komercyjnym. poki co wyglada to dość mizernie ale euforia przeplata z dołkami i tak w koło.

            niemniej wiele przez te ostatnie tygodnie sie nauczylem ( w skali roku) i to była dobra decyzja. to tak na mariginesie.

            czekam na kolejne tematy.

            ps.: ciesze sie, ze zdecydowałeś się na polskojęzyczną wersję bloga.

      • Łukasz Kasprzycki

        Hej,zainstalowałem wersje react-routera 0.14.6 i taką samą historie.Dodało to kilka brakujących biblkiotek (np. IndexRoute),ale wciąż brakuje hashHistory/browserHistory.Czy może to wynikać z tego że mam najnowszą wersje Node.js?A może da się pobrać skądś te biblioteki,bo to dosć ważne dla mnie żeby móc ich używać w projekcie..?

Google Analytics Alternative