React Router
Permettre de passer d’une page à l’autre dans votre application React.
Manuel
Pour l'installer dans votre projet :
Exemple de SPA avec Router
import {
BrowserRouter,
Routes,
Route,
Outlet,
useParams,
} from 'react-router-dom';
import './App.css';
function Modele() {
return (
<div>
<a href="/">Page principale</a>
<a href="/dadams">Douglas Adams</a>
<a href="/oscard">Orson Scott Card</a>
<br />
<Outlet />
</div>
);
}
function PagePrincipale() {
return <h1>Page principale</h1>;
}
function DouglasAdams() {
return (
<>
<h1>Page de Douglas Adams</h1>
<a href="/livre/1">Livre 1</a>
<a href="/livre/2">Livre 2</a>
</>
);
}
function OrsonScottCard() {
return <h1>Page de Orson Scott Card</h1>;
}
function Livre() {
const { id } = useParams();
return <h1>Livre #{id}</h1>;
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Modele />}>
<Route index element={<PagePrincipale />} />
<Route path="dadams" element={<DouglasAdams />} />
<Route path="oscard" element={<OrsonScottCard />} />
<Route path="livre/:id" element={<Livre />} />
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
CodeSandbox
Différents éléments du projet monrouteur
BrowserRouter
Un <BrowserRouter> stocke l'emplacement actuel dans la barre d'adresse du navigateur en utilisant des URL propres et navigue en utilisant la pile d'historique intégrée du navigateur.
function App() {
return (
<BrowserRouter>
// Le reste de l'application React
</BrowserRouter>
);
}
Manuel
Routes
L'élément <Routes> (au pluriel) indique l'ensemble des routes qui seront disponibles dans l'application. Doit contenir au moins un élément <Route> (au singulier).
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Modele />}/>
</Routes>
</BrowserRouter>
);
}
Route
L'élément <Route> (au singulier) définit une page de l'application à afficher selon son URL.
Par exemple :
Cette route indique que http://serveur/dadams pointe sur la composante DouglasAdams.
Manuel
Routes imbriquées
L'élément <Route> (au singulier) peut contenir des routes enfants. Comme ceci :
<Route path="/" element={<Modele />}>
<Route index element={<PagePrincipale />} />
<Route path="dadams" element={<DouglasAdams />} />
<Route path="oscard" element={<OrsonScottCard />} />
<Route path="livre/:id" element={<Livre />} />
</Route>
Plusieurs choses importantes ici :
-
La route "/" est la route parent des autres. Lorsqu'un élément est mentionné dans la route parent, l'élément est généré avant celui de la route enfant. Il faut par contre indiquer dans l'élément parent à quel endroit faut-il générer l'élément enfant (avec l'élément <Outlet>) :
-
Le mot clé index indique l'élément qui sera généré lorsque l'utilisateur navigue à l'URL du parent.
Routes dynamiques
On peut placer un paramètre dans une route qui peut être lu par l'élément généré.
Dans la route, on peut avoir le paramètre :id :
Dans l'élément, on accède au paramètre avec la fonction useParams() :
useContext
Une façon de passer des données d'un élément parent vers un élément enfant est via les props. Le flux normal de données dans React est de haut en bas. Les éléments en bas ne peuvent pas influencer les données venant de plus haut.
Lorsque c'est requis pour un élément enfant d'influencer les données venant d'un parent, React offre une fonctionnalité qui se nomme contexte. Le contexte rend possible la mise à jour d'états par tous les sous-éléments selon les besoins.
Manuel
Prenons l'exemple suivant :
graph TD
A[Home] --> B[Panier];
A[Home] --> C[Fiche];
B[Panier] --> D[Fiche];
L'élément Home a un panier contenant les items désirés par l'utilisateur. L'élément Fiche représente un item avec une photo, une description et un prix. L'élément Panier contient les fiches des items ajoutés par l'utilisateur.
Si nous voulons que le bouton Ajouter au panier de l'élément Fiche puisse influencer le contenu du Panier, il faut créer un contexte :
graph TD
Z[ContextePanier] --> A[Home];
A[Home] --> B[Panier];
A[Home] --> C[Fiche];
B[Panier] --> D[Fiche];
Tous les éléments sous ContextePanier peuvent accèder au contenu et le modifier au besoin. Dans cette situation, la liste des items au panier vient du contexte et est utilisé dans l'élément Panier alors que Fiche s'ajoute ou se retire du panier via le contexte.
Démo de useContext
Voici les éléments pertinents pour l'utilisation de contexte :
import React, { useState } from 'react';
interface IItemPanier {
id: number;
nom: string;
photo: string;
prix: number;
quantite: number;
}
export type PanierContextType = {
itemsPanier: IItemPanier[];
panierOuvert: boolean;
setItemsPanier: (itemsPanier: IItemPanier[]) => void;
setPanierOuvert: (ouvert: boolean) => void;
};
const panierVide: IItemPanier[] = [];
export const PanierContext = React.createContext<PanierContextType>({
itemsPanier: panierVide,
panierOuvert: false,
setItemsPanier: () => {},
setPanierOuvert: () => {},
});
export default function PanierProvider(props: any) {
const [itemsPanier, setItemsPanier] = useState(panierVide);
const [panierOuvert, setPanierOuvert] = useState(false);
const values = {
itemsPanier,
panierOuvert,
setItemsPanier,
setPanierOuvert,
};
return (
<PanierContext.Provider value={values}>
{props.children}
</PanierContext.Provider>
);
}
import './App.css';
import PanierProvider from './contexts/panier.context';
import Home from './components/home.component';
function App() {
return (
<PanierProvider>
<Home />
</PanierProvider>
);
}
export default App;
import { useContext } from 'react';
import { Drawer } from '@mui/material';
import { PanierContext } from '../contexts/panier.context';
import Fiche from './fiche.component';
import { Box } from '@mui/material';
export default function Panier() {
const { itemsPanier, panierOuvert, setPanierOuvert } =
useContext(PanierContext);
return (
<Drawer
anchor="right"
open={panierOuvert}
onClose={() => {
setPanierOuvert(false);
}}
>
<Box sx={{ width: 300 }}>
{itemsPanier &&
itemsPanier.map((item) => {
return <Fiche chapeau={item} dansPanier={true} />;
})}
</Box>
</Drawer>
);
}
import { useContext } from 'react';
import { Card } from '@mui/material';
import { CardActions } from '@mui/material';
import { CardContent } from '@mui/material';
import { CardMedia } from '@mui/material';
import { Button } from '@mui/material';
import { Typography } from '@mui/material';
import { PanierContext } from '../contexts/panier.context';
import { IChapeau } from '../models/ichapeau.model';
interface IFiche {
chapeau: IChapeau;
dansPanier: boolean;
}
export default function Fiche(props: IFiche) {
const { itemsPanier, setItemsPanier } = useContext(PanierContext);
const ajouterAuPanier = () => {
const nouveauPanier = [...itemsPanier, { ...props.chapeau, quantite: 1 }];
console.log(nouveauPanier);
setItemsPanier(nouveauPanier);
};
const retirerDuPanier = () => {
var i = 0;
console.log('retirer du panier : ', props.chapeau.id);
while (i < itemsPanier.length) {
if (itemsPanier[i].id === props.chapeau.id) {
itemsPanier.splice(i, 1);
} else {
++i;
}
}
const nouveauPanier = [...itemsPanier];
setItemsPanier(nouveauPanier);
};
return (
<Card sx={{ width: 300, maxWidth: 300, height: 300, maxHeight: 300 }}>
<CardMedia
component="img"
height="150"
sx={{ objectFit: 'contain' }}
image={props.chapeau.photo}
/>
<CardContent>
<Typography gutterBottom variant="h6" component="div">
{props.chapeau.nom}
</Typography>
<Typography variant="body2" color="text.secondary">
{props.chapeau.prix} $
</Typography>
</CardContent>
<CardActions>
{!props.dansPanier && (
<Button
size="small"
color="primary"
onClick={() => ajouterAuPanier()}
>
Ajouter au panier
</Button>
)}
{props.dansPanier && (
<Button
size="small"
color="primary"
onClick={() => retirerDuPanier()}
>
Retirer du panier
</Button>
)}
</CardActions>
</Card>
);
}
CodeSandbox
Se connecter à un API
Il est préférable d’utiliser la librairie Axios pour aller chercher vos données de l’API :
axios.get('https://bieres.profinfo.ca/api/bieres').then((response) => {
setListeBieres(response.data.bieres);
});
Manuel