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