
Od czasu kiedy napisałemwpis wprowadzający do biblioteki react-router minęło już trochę czasu. Na tyle dużo, że w tak zwanym “międzyczasie” jej autorzy zdążyli wypuścić nową wersję. Niestety zawiera ona sporo “łamiących zmian” co powoduje, że tamten wpis stracił na aktualności. Dlatego też dziś przedstawię podstawy react-router w wersji 4, tak aby pozostać zawsze na czasie!

React-router w wersji 4 czyli wszystko od nowa...
Od czasu kiedy napisałemwpis wprowadzający do biblioteki react-router minęło już trochę czasu. Na tyle dużo, że w tak zwanym “międzyczasie” jej autorzy zdążyli wypuścić nową wersję. Niestety zawiera ona sporo “łamiących zmian” co powoduje, że tamten wpis stracił na aktualności. Dlatego też dziś przedstawię podstawy react-router w wersji 4, tak aby pozostać zawsze na czasie!
Wstęp
Na wstępie napiszę, że przykład prezentowany w dzisiejszych rozważaniach oparłem na prostym starterze React dostarczonym przez samego Facebooka czyli create-react-app. Poza tym na końcu wpisu znajdziesz link do repozytorium GitHuba, w którym znajduje się omawiany dzisiaj kod. Będziesz więc mógł samodzielnie go sobie “poklikać”.
Tworząc aplikację webową, do pracy potrzebna nam będzie webowa wersja react-router. Aby jej użyć, musimy zainstalować trochę inny pakiet npm niż robiliśmy to dotychczas:
yarn add react-router-dom
lub jeśli wolisz używać starego dobrego npm, to wówczas:
npm install --save react-router-dom
Podstawowa konfiguracja
Nowa wersja omawianej biblioteki została zmieniona w sposób, który powoduje, że za jej pomocą można budować “routing” w sposób bardziej deklaratywny. Odbywa się to bowiem w komponentach aplikacji.
Załóżmy, że posiadamy dwa komponenty główne: Home.js
który miałby być przypisany do ścieżki /
oraz About.js
wyświetlany przez ścieżkę /about
. Poniżej kod komponentu Home.js
:
import React from 'react';
const Home = () => {
return (
<div className="container">
<h1>Home!!</h1>
</div>
);
}
export default Home;
A tutaj kod komponentu About.js
:
import React from 'react';
const About = () => {
return (
<div className="container">
<h1>About!!</h1>
</div>
);
}
export default About;
Jak widzisz, są to zwykłe komponenty prezentacyjne, które różnią się tylko tekstem wyświetlanym na ekranie. Na potrzeby wpisu będą one dla nas wystarczające.
Renderowanie aplikacji
Przypomnijmy sobie teraz jak wyglądała konfiguracja “routingu” w starszych wersjach biblioteki react-router (kod bezpośrednio z przykładu z mojego starego wpisu na ten temat):
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, browserHistory } from 'react-router'
import App from './components/Main';
ReactDOM.render((
<Router history={browserHistory}>
<Route path="/" component={App} />
</Router>
), document.getElementById('app'));
Definicja “routingu” w tamtym przypadku odbywała się poprzez wyrenderowanie komponentu Router
, wewnątrz którego, za pomocą komponentów Route
definiowaliśmy poszczególne ścieżki i wiązaliśmy je z komponentami.
Teraz ciężar definiowania ścieżek został przeniesiony bezpośrednio do zainteresowanych komponentów. Załóżmy, że posiadamy główny komponent całej aplikacji o nazwie App.js
. Komponent ten renderujemy zupełnie standardowo (zwyczajowo w pliku index.js
):
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
Definicja routingu
W tym momencie nie dokonujemy już definicji “routingu” w momencie renderowania głównego komponentu aplikacji. Wszystko przenosi się do komponentu App.js
:
import React, { Component } from 'react';
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom';
import Home from './Home';
import About from './About';
class App extends Component {
render() {
return (
<Router>
<div className="container">
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</div>
</Router>
);
}
}
export default App;
Importy
Na początek zwróć uwagę na importy z pakietu react-router-dom
. Pierwszy z nich to BrowserRouter
, który od razu przemianowujemy na, po prostu, Router
. Jest to główny komponent, w którym umieszczamy całą definicję naszego “routingu”.
Kolejny element to Route
, który pozwala na zdefiniowanie poszczególnych ścieżek i powiązanie ich z komponentami. O tym jeszcze trochę za chwilę.
Importujemy też obiekt Link
ale jeszcze go nie wykorzystujemy. O tym też opowiem za moment.
Metoda render
Przejdźmy teraz do metody render
komponentu App.js
. Jak widzisz, zwraca ona komponent BrowserRouter
(przemianowany przy imporcie na Router
). W odróżnieniu od starej wersji biblioteki, nie przekazujemy już do niego obiektu historii. Jest on bowiem pre-definiowany przez komponent BrowserRouter
. Po zajrzeniu do jego implementacji łatwo stwierdzić jak można ominąć to zachowanie i zmienić domyślny obiekt historii (można zaimportować obiekt Router
i przekazać mu historię w stary sposób).
Komponent BrowserRouter
zawiera element div
i dopiero w nim znajdujemy definicje poszczególnych ścieżek. Dlaczego tak? Otóż działa to w ten sposób: React renderuje komponent App.js
, umieszcza w drzewie DOM element div
, a w nim wyświetla odpowiedni komponent(y) w zależności od aktualnej ścieżki. Czyli jeśli będzie to /
to w tym miejscu pokaże się komponent Home.js
, a jeśli /about
to komponent About.js
.
Zwróć też uwagę na atrybut exact
komponentu Route
użyty w pierwszej ze ścieżek. Najlepiej będzie jeśli sam przetestujesz co się stanie jeśli by go zabrakło… Jeśli jednak jesteś leniwy to wyjaśniam - wymusza on dokładne porównanie ścieżek, które nie jest włączone domyślnie. Jeśli by tego atrybutu zabrakło, wówczas dla ścieżki /about
obie definicje ścieżek są spełnione (/
zawiera się w /about
) i wyświetlone zostałyby oba komponenty. Warto o tym pamiętać.
Layout czyli kod wspólny dla każdej z podstron
Takie podejście do “routingu” daje nam pewne możliwości, które wcześniej także były do osiągnięcia ale w trochę mniej wygodny sposób…
Komponent App.js
jest bowiem w tym momencie “placeholderem” na kod wyświetlany zależnie od aktualnej ścieżki. Wydaje się więc idealnym miejscem na umieszczenie na przykład “sidebara” lub menu strony, która jest taka sama dla każdej z podstron:
import React, { Component } from 'react';
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom';
import Home from './Home';
import About from './About';
class App extends Component {
render() {
return (
<Router>
<div className="container">
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</div>
</Router>
);
}
}
export default App;
W powyższym przykładzie, do kontenera wyświetlanego przez komponent dodałem listę zawierającą linki do poszczególnych ścieżek. Lista ta jest renderowana niezależnie od aktualnej ścieżki ponieważ, odpowiedni komponent wyświetlany jest w miejscu użycia komponentu Route
!
Tutaj uwaga dla tych, którzy nie czytali mojego poprzedniego tekstu o react-router: do przełączania się pomiędzy ścieżkami “routingu” służy właśnie komponent Link
. Jeśli użyłbyś zwykłego elementu a
, to nastąpiłoby pełne przeładowanie strony. Musiałbyś więc obsługiwać zdarzenie onClick
tego elementu i wykorzystywać obiekt history
do wykonania przekierowania… O obiekcie history
słów kilka za moment.
Dostęp do parametrów przekazywanych w adresie
Jak w każdym systemie “routingu” często sam adres zawiera pewne specyficzne informacje. Na przykład adres: http://example.com/user/2
zawiera index użytkownika (wartość 2
). W takim przypadku, ścieżkę definiuje się w taki sposób:
<Route path="user/:id" component={User}/>
Za pomocą powyższego kodu informujemy react-router
, że ścieżka ta powinna zawierać parametr id
(dwukropek informuje, że jest to parametr).
Później, w komponencie User
do wartości id
możemy dostać się poprzez this.props
:
<p>User id: {this.props.match.params.id}</p>
Jak możesz zauważyć, obiekt this.props
komponentu wyświetlanego w ramach “routingu” zawiera obiekt match
. Posiada on kilka przydatnych właściwości, a jedną z nich jest obiekt params
, który to z kolei posiada wszystkie parametry przekazane w adresie (o ile tak zdefiniowaliśmy).
Dostęp do obiektu history
Na koniec jeszcze jedno… Częstym przypadkiem użycia jest sytuacja kiedy potrzebujemy w naszym kodzie wykonać “ręczne” przekierowanie do odpowiedniej ścieżki. Robi się to dokładnie tak samo jak dotychczas:
componentDidMount() {
this.context.history.push('/');
}
Obiekt context
jest specjalnym obiektem, który wstrzykiwany jest do każdego komponentu objętego “routingiem”.
React-router w wersji 4 - podsumowanie
Nowa wersja opisywanej biblioteki react-router trochę się zmieniła ale wydaje mi się, że dla kogoś kto korzystał już ze starszych jej wersji odnalezienie się wśród tych wszystkich nowości nie będzie problemem. Dla “świeżaków” natomiast nie powinna ona stanowić większego wyzwania niż wcześniej. Wszystkich zainteresowanych zachęcam do przejrzenia dokumentacji oraz do zapoznania się z kodem dzisiejszego przykładu, jaki zamieściłem w repozytorium GitHub.
A może chciałbyś lepiej poznać react-router?
Powyższy wpis to tylko niezbędne minimum, które trzeba znać aby zacząć pracować z biblioteką react-router. Jeśli chciałbyś dogłębnie i od podstaw poznać tę bibliotekę to specjalnie dla Ciebie przygotowałem specjalne kursy on-line, dzięki którym od podstaw nauczysz się react-routera, ale też Reacta oraz Reduxa! Kliknij czerwony guzik w “boksie” poniżej aby dowiedzieć się więcej: