wersja: 18.x.x
(listopad 2022)
v.6.1.1
React Router to zestaw komponentów, które umożliwiają nawigację w aplikacji, czyli możemy tworzyć podstrony.
Router to główny komponent, dzięki któremu możemy korzystać z dobrodziejstw routingu.
W przypadku projektów internetowych, react-router-dom udostępnia routery <BrowserRouter> i <HashRouter>. Główna różnica między nimi polega na sposobie przechowywania adresu URL i komunikacji z serwerem.
Używa zwykłych ścieżek URL. np. www.nis.pl/about, ale wymagają one prawidłowego skonfigurowania serwera.
Aplikacja Create React App ma od razu przy instalacji skonfigurowane środowisko do użycia tego routera. Zawiera też instrukcje dotyczące konfiguracji serwera produkcyjnego.
Przechowuje bieżącą lokalizację w części za # np. www.nis.pl/#/about.
Ten rodzaj nie wymaga konfiguracji serwera.
npm install react-router-domInstalacja
Komponenty
Home
About
Contact
Navigation.js
Place for clicked item in navigation
Default page is HOME
Home.js
App.js
Stwórzmy najpierw komponenty odpowiedzialne za wyświetlanie poszczególnych podstron
//Home.js
const Home = () => <div>Home</div>
export default Home;//About.js
const About = () => <div>About</div>
export default About//Contact.js
import React from 'react'
const Contact = () => <div>Contact</div>
export default Contact<Link to="/about">About</Link>Komponent Link jest odpowiedzialny za przejście do innej podstrony.
Należy przekazać props "to", w którym będzie adres podstrony.
Komponent Link jest renderowany oczywiście do tagu <a>
Stwórzmy komponent z nawigacją
//Navigation.js
import React from 'react';
import { Link } from 'react-router-dom';
const Navigation = () => {
return <ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/contact">Contact</Link></li>
</ul>
}
export default Navigation;Dostosowujemy komponent App do tego, aby wykorzystywał routing. Na początek importy
//Plik App.js
import React from "react";
import {
BrowserRouter as Router,
Routes,
Route
} from "react-router-dom";
import Home from './components/router/Home';
import About from './components/router/About';
import Contact from './components/router/Contact';
import Navigation from './components/router/Navigation';
//..... dalszy ciąg za chwilęTworzymy alias do BrowserRouter. Będzie to główny komponent aplikacji wykorzystującej Routing
Za chwile o nich więcej
W App wykorzystujemy dostępne komponenty z biblioteki
react-router-dom
//... dalsza część pliku App.js
const App = () => {
return <Router>
<div>
<Navigation />
<Routes>
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route exact path="/" element={<Home />} />
</Routes>
</div>
</Router>
}
export default App<Routes> pozwala przejrzeć dostępne możliwości podstron i wyrenderować pierwszy, który będzie pasował do bieżącego adresu URL
<Route> pozwala dopasować ścieżkę do komponentu
Ważną rzeczą do zapamiętania jest to, że <Route path> pasuje do początku adresu URL, a nie do całości.
Dlatego <Route path = "/"> będzie pasować do każdego adresu URL.
Z tego powodu zazwyczaj umieszczamy ten <Route> jako ostatni w <Routes> lub korzystamy z właściwości exact, która dopasowuje dokładnie taką ścieżkę.
Jeśli dany link już nie istnieje lub ktoś wpisał go błędnie wtedy otrzymujemy zazwyczaj błąd 404, czyli Not Found (nie znaleziono).
Spróbuj w naszej aplikacji wpisać adres:
http://localhost:3000/blog
Póki co nic się nie stanie lub zobaczysz pustą stronę.
Jak widzisz – React Router nie umie odnaleźć ścieżki.
Możemy stworzyć własną podstronę, która będzie informowała o tym, że dana ścieżka jest niepoprawna.
Stworzymy do tego dodatkowy komponent – np. NotFound.
const NotFound = () => <div>NotFound</div>
export default NotFound;<Route path="*" element={<NotFound />} />Zmodyfikujemy teraz routing w następujący sposób:
Jedną z najważniejszych rzeczy w routingu jest możliwość przekazywania parametrów w URL.
Możemy je porównać do zmiennych, które w przeciwieństwie do pozostałej części adresu mogą się po prostu zmieniać.
Spójrzmy na stronę:
https://twitter.com/kowalski_it oraz na https://twitter.com/thecodinglove
Jeśli zmieniamy użytkownika na inną osobę zauważymy, że komponent jest ten sam, zmieniają się tylko dynamicznie dane.
Jeśli chcemy przyjąć parametr, wystarczy część ścieżki poprzedzić znakiem dwukropka ":".
Przykład:
<Route path="/" element={ <UserInfo />} />
<Route path="user" element={ <UserDetails />} >
<Route path=":invoiceId" element={<UserDetails />} />
</Route>Za każdym razem kiedy użytkownik wpisze adres pasujący do danego wzorca (/kowal, /johnsnow, /abc itp) , zostanie załadowany komponent UserInfo.
Aby odebrać parametr w danym komponencie wykorzystamy Hook useParams()
const UserDetails = () => {
let {name} = useParams();
return (
<div>
<h3>User {name}</h3>
</div>
);
}(Zagnieżdżanie elementów)
<Route path='/'>
<Home /> // || null
</Route>Pamiętamy, że każdy element Route dopasowuje ścieżkę do adresu url i w zależności od tego czy pasuje ona do wzorca renderuje element bądź null
Wyobraźmy sobie taką sytuację:
Home
Blog
Navigation.js
Movies
Songs
Series
Path: blog/movies
Something about Movies
Blog
Blog.js
Topic.js
App.js
W komponencie App nie zmieniamy za wiele dodajemy po prostu komponent Blog wskazując na jego ścieżkę w routingu
<Routes>
<Route path="/" element={<Home />}>
<Route path="blog" element={<Blog />}>
<Route path="movies" element={<Movies />} />
<Route path="songs" element={<Songs />} />
<Route path="series" element={<Series />} />
</Route>
</Routes>
Context API pozwala nam stworzyć wspólny stan dla określonej liczby komponentów. Oczywiście Context może też obejmować całą aplikację.
Na Context składają się dwie cześci: ContextProvider i ContextConsumer
ContextProvider – okala komponenty w których kontekst ma być dostępny.
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}Zawartość zmiennej themes
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
ContextConsumer – konsument kontekstu
import {ThemeContext} from './theme-context';
function ThemeTogglerButton() {
return (
<ThemeContext.Consumer>
{({theme}) => (
<button
style={{backgroundColor: theme.dark.background}}>
Przełącz motyw
</button>
)}
</ThemeContext.Consumer>
);
}
export default ThemeTogglerButton;
Zapis z poprzedniego slajdu nie jest niestety wygodny – z pomoca przychodzą nam jednak hooki.
import {ThemeContext} from './theme-context';
function ThemeTogglerButton() {
const context = useContext(ThemeContext);
return (
<button
style={{backgroundColor: context.theme.dark.background}}>
Przełącz motyw
</button>
);
}
export default ThemeTogglerButton;Hook useReducer jest bardzo podobny do hooka useState. Można powiedzieć, że jest jego znacznie rozbudowaną wersją. W pewnym sensie przypomina też funkcję reducera znane z biblioteki Redux.
Pierwszy argument dla hooka to reducer czyli funkcja która w zależności od podjętej akcji przeprowadza odpowiednią mutację stanu.
Drugi to initialState – czyli stan początkowy. Możesz teraz zmienić stan w nieco bardziej deklaratywny sposób.
Hook'a useReducer warto używać w sytuacjach kiedy nasz stan jest skomplikowany lub gdy jego przekształcenia są skomplikowane i uzależnione od wielu czynników.
PropTypes jest biblioteką, która pozwala na sprawdzanie typów w aplikacji. Jeśli chcemy używać jej w naszym repozytorium musimy zainstalować bibliotekę prop-types za pomocą managera pakietów npm.
Używamy jej żeby kontrolować typy propsów w komponentach.
npm install prop-types --save-devPonadto jeśli chcemy mieć pewność, że została dostarczona wartość, możemy dodać wywołanie .isRequired na końcu każdej z wymienionych na poprzednim slajdzie opcji.
App.propTypes = {
name: PropTypes.string.isRequired
}Jeżeli nie podamy wymaganej wartości dla danego propsa w konsoli zostanie wygenerowany komunikat błędu.
index.js:1 Warnin: Filed prop type:
The prop name is marked as required
in App, but its value is undefined.Jeżeli typ propsa nie ma znaczenie, ważne jest, żeby props był przekazany to możemy sprawdzić to za pomocą any.
App.propTypes = {
name: PropTypes.any.isRequired
}Jeżeli zależy nam na konkretnej wartości propsa możemy to z walidować za pomocą opcji oneOf. Jeśli zostanie przekazana opcja inna niż zawarta w tablicy, zostanie wygenerowany komunikat ostrzeżenia.
App.propTypes = {
name: PropTypes.oneOf(
["Rambo", "Terminator"])
}import PropTypes from "prop-types";
const User = ({name, surname, age}) => {
return (
<>
<p>{name}</p>
<p>{surname}</p>
<p>{age}</p>
</>
)
}
User.propTypes - {
name: PropTypes.string,
surname: PropTypes.string,
age: PropTypes.number
}Komponenty wyższego rzędu (HOC) to technika dzięki, której możemy wielokrotnie wykorzystywać logikę komponentu.
HOC to funkcja/component, który jako parametr przyjmuje komponent i zwraca nowy komponent
const newComponent = hoc(WrappedComponent);Korzystając z porównania możemy powiedzieć że:
const ironMan = withSuit(TonyStark);const updatedComponent = OriginalComponent => {
const NewComponent = (props) => {
return <OriginalComponent {...props}/>
}
return NewComponent
}Podstawowy szablon HOC
Tutaj funkcja przyjmuje komponent OriginalComponent jako parametr i zwraca go w niezmienionym stanie
Zalet samego testowania jest mnóstwo np.: eliminacja częściowa regresji, łatwiejsze dodawanie nowych funkcji, testy służące jako dokumentacja itp.
Innymi słowy testowanie sprawia, że aplikacja jest mniej podatna na błędy. Sprawdzamy czy nasz kod robi to, co chcemy, i czy aplikacja działa zgodnie z przeznaczeniem.
Tego typu testy sprawdzają poszczególne fragmenty lub komponenty oprogramowania.
Fragmentem może być funkcja, metoda, procedura, moduł lub obiekt.
Testowanie modułowe sprawdza funkcjonalność poszczególnych części aplikacji.
Testy są wykonywane na każdym komponencie w oderwaniu od innych.
Aplikacje React składają się z wielu komponentów, tak więc testowanie komponentów polega na testowaniu ich indywidualnie.
Jest to lekki i prosty framework służący do testowania języka JavaScript. Zawiera test runner na bazie Node, tzn. że nie potrzebujemy przeglądarki żeby uruchamiać kod.
Aby wygodnie pisać testy potrzebujemy:
React Testing Library jest zestawem funkcji pomocniczych, które pozwalają nad testowanie komponentów reactowych bez polegania na ich szczegółach implementacyjnych (ang. implementation details). Doskonale sprawdza się w połączeniu z Jestem i jego funkcjonalnością mockowania modułów.
Dobra wiadomość jest taka, że Jest i React Testing Library są zainstalowane, skonfigurowane i gotowe do pracy w aplikacji create-reacta-app.
Pierwszy test w pliku multiplyByTwo.test.js
//Zaimportowana funkcja
function multiplyByTwo = (x) => {
return x*2;
}
test("Mnożenie przez 2", () => {
expect(multiplyByTwo(2).toBe(9))
});Podczas pisania testów sprawdzamy czy wartości spełniają określone warunki. Funkcja expect daje dostęp do szeregu „dopasowań”, które pozwalają zweryfikować wiele rzeczy.
expect(100).toBeWithinRange(90, 110);
expect(2===2).toEqual(true);Więcej: https://jestjs.io/docs/en/expect#expectvalue
expect().toBe()Jest będzie automatycznie uruchamiał testy spełniające konwencję nazewniczą.
import { render, screen } from '@testing-library/react';
import Home from './Home';
test('renders Home', () => {
render(<Home />);
const el = screen.getByText('To jest komponent home.');
expect(el).toBeInTheDocument();
});