Créez une application React
Redux fonctionne indépendamment de React. Nous avons découvert précédemment les concepts qui fondent son fonctionnement. Nous savons ainsi que nous pouvons :
stocker dans un store centralisé des états ;
exécuter des parties de notre code en fonction des changements intervenant dans le store ;
modifier les valeurs du store en appliquant des actions.
Créons alors notre application React. Pour ce faire, vous pouvez vous référer au cours Débutez avec React, ou suivre les commandes ci-dessous :
## create react project npm init react-app resto-cmd # ou yarn react-app resto-cmd # ou encore npx create-react-app resto-cmd
Nous allons ajouter notre package Redux Toolkit pour bénéficier de Redux dans notre application React et du package react-redux
, qui va nous permettre de manipuler le store dans nos composants :
Pour ce faire, utilisez la commande suivante :
## add rtk as package npm install @reduxjs/toolkit react-redux # ou yarn add @reduxjs/toolkit react-redux
À ce stade, vous devriez obtenir une application React prête à être développée avec notre state manager Redux.
Afin de préparer la suite de nos avancées, nous allons utiliser une partie du code que nous avons déjà implémenté !
Afin de suivre les bonnes pratiques, nous allons respecter certaines règles proposées dans le guide de styles que vous retrouvez dans la documentation de Redux (en anglais).
D’abord, créons un dossier
app/
.Nous y déplaçons nos fichiers
App.js
etApp.css
en modifiant leurs imports.Ensuite, nous créons notre fichier
app/store.js
.Créons un dossier
common
pour y placer notre fichiermodels.js
, car il sera utile à plusieurs parties de notre application et tous les composants réutilisables. Nous ajoutons nos produits dans ce fichier.Puis, en copiant l’intégralité de notre fichier
flux_s6.js
, nous ajoutons ce code dans le fichierstore.js
. J'en profite pour rajouter deux produits par défaut dans mon store. Vous pouvez voir dans le code qui suit comment je m’y prends.Nous modifions notre import du composant App dans
index.js
qui devient./app/App.js
.Nous ajoutons le provider dans notre fichier
app/App.js
comme suit, et nettoyons un peu son implémentation :
import { Provider } from 'react-redux'
import { store } from './store';
import './App.css';
function App() {
return (
<Provider store={store}>
<div className="App">
</div>
</Provider>
);
}
export default App;
Vous pouvez suivre le screencast suivant pour comprendre comment je m’y prends :
Vous devriez à ce stade avoir :
1. Un fichier app/store.js
comme suit :
import { configureStore } from "@reduxjs/toolkit"
import { PouletCroquant, SuperCremeux } from "../common/models";
let state = {
value: null,
list: [
SuperCremeux,
PouletCroquant,
]
};
const reducer = (currentState, action ) => {
switch (action.type) {
case 'ADD_PRODUCT':
const listWithNewProduct = [...currentState.list, action.payload]
return {...currentState, list: listWithNewProduct}
case 'REMOVE_PRODUCT':
const list = currentState.list.filter(
(item, index) => index !== action.payload
)
return {...currentState, list: list}
case 'APPLY_VOUCHER':
const withVoucherList = currentState.list.map(
item => item.title === 'Super Crémeux' ? ({...item, price: action.payload.price}) : item
)
return {...currentState, list: withVoucherList}
case 'UPDATE_FIRSTNAME':
const owner = {...currentState.owner, firstName: action.payload}
return {...currentState, owner}
default:
return currentState
}
}
export const store = configureStore(
{
preloadedState: state,
reducer
}
)
2. Un fichier common/models.js
:
export const DoubleCantal = {
title: 'Double Cantal',
price: 15.99,
}
export const SuperCremeux = {
title: 'Super Crémeux',
price: 14.99,
}
export const PouletCroquant = {
title: 'Poulet Croquant',
price: 17.99,
}
3. Un fichier app/App.js
:
import { Provider } from 'react-redux'
import { store } from './store';
import './App.css';
function App() {
return (
<Provider store={store}>
<div className="App">
</div>
</Provider>
);
}
export default App;
Nous venons d’intégrer nos sources à notre projet React. Ainsi, nous avons :
construit un fichier
store.js
pour y placer notre reducer et notre configuration du store ;appliqué notre provider à la racine de notre application (
App.js
) afin de pouvoir accéder à notre store au sein de chacun de nos composants ;placé nos modèles dans un fichier dédié pour mieux structurer notre code ;
ajouté le style de l’application pour utiliser les classes toutes prêtes.
Et voilà, nous sommes enfin prêts à manipuler notre state dans React avec Redux et Redux ToolKit !
Associez le store à un premier composant
Maintenant que notre store est configuré, notre prochain défi sera de…
… l’associer à l’un de nos composants ?
Exact !
Pour accéder au store de Redux dans nos composants, nous pouvons utiliser le hook useStore
de react-redux que nous avons précédemment installé.
Ci-dessous, un exemple d’implémentation permettant d’accéder au store.
import { useStore } from "react-redux";
const Component = () => {
const store = useStore();
return <i></i>
};
useStore
nous permet donc d’accéder à l’instance du store qui est diffusée dans notre application via le Provider préalablement inséré.
Maintenant que nous savons comment accéder au store, nous allons commencer à l’utiliser dans notre application.
Pour cela, nous allons créer notre composant affichant la liste des produits sélectionnés, que nous appellerons Cart
, dans le fichier features/cart/Cart.js
.
L’instance store
fournit une méthode getState()
qui nous permet d’obtenir à chaque instance les valeurs stockées dans le store. Si nous faisons donc appel à store.getState()
, nous aurons accès à list
qui contient notre liste de produits sélectionnés.
Il ne nous reste donc plus qu’à “mapper” sur store.getState().list
en faisant :
store.getState().list.map((item, index) => <JSXElement />
Je vous propose de suivre le screencast suivant pour comprendre comment le créer.
Nous venons donc d’ajouter l’affichage du listing de nos produits sélectionnés dans notre panier.
Faisons le point sur cette phase d’implémentation, au cours de laquelle nous avons :
Déclaré et inséré notre composant Cart dans
App.js
.Créé notre composant Cart dans
Cart.js
.Importé
useStore
de react-redux.Utilisé
useStore
pour l’assigner à notre variable locale store.Utilisé la méthode
getState
pour récupérer l’état (le state) courant.Mappé sur list du store qui contient la liste des produits sélectionnés.
Ce qui nous donne un fichier app/App.js
:
import { Provider } from 'react-redux'
import { store } from './store';
import './App.css';
import { Cart } from '.features/cart/Cart';
function App() {
return (
<Provider store={store}>
<div className="App">
<Cart />
</div>
</Provider>
);
}
export default App;
Ainsi qu'un fichier features/cart/Cart.js
:
import { useStore } from "react-redux";
export const Cart = () => {
const store = useStore();
return <div className="Selection">
<h1>Liste de produits sélectionnés</h1>
{
store.getState().list.map(
(item, index) => <span key={index} className="SelectedProduct">{item.title} {item.price} €</span>
)
}
</div>
};
Faites des modifications du store à partir de votre votre composant
Nous voilà capables d’afficher la liste de produits sélectionnés… enfin, presque ! La liste contient des produits insérés manuellement avec le code.
Mais nous souhaitons rendre cette liste dynamique, c'est-à-dire être capables d’ajouter d'autres produits sans pour cela réécrire de code pour en ajouter.
Pour ajouter cette fonctionnalité, nous allons créer un bouton pour chaque produit et utiliser le store pour stocker le produit correspondant à chaque bouton lors du clic sur le bouton.
À l’instar de la méthode getState
, l’instance store
fournit une méthode dispatch qui va nous permettre d’appliquer des actions, dispatch()
.
Bien évidemment, la méthode dispatch fonctionne de pair avec la méthode subscribe. On va donc modifier notre accès à list
et ajouter un état local qui sera modifié à chaque dispatch
.
On ajoute donc :
const [list, setList] = useState(store.getState().list);
Et afin de venir modifier notre state à chaque changement du store, on ajoute à notre composant :
useEffect(() => {
store.subscribe(() => { setList(store.getState().list) })
}, [store])
Pour ajouter un produit, nous allons créer un bouton et ajouter un dispatch
de type ADD_PRODUCT
avec comme payload le modèle ‘SuperCremeux’ au clic sur celui-ci :
<div className="CartNavBar">
<button onClick={() => store.dispatch({type: 'ADD_PRODUCT', payload: SuperCremeux})}>Ajouter un super crémeux</button>
</div>
Je vous propose de suivre le screencast suivant pour bien comprendre :
Revoyons ensemble ce que nous venons de réaliser :
Nous avons connecté notre composant au store pour écouter chaque changement de valeur de celui-ci.
Nous avons exécuté une action d’ajout de produit pour modifier la valeur du store.
Ce qui nous donne dans notre fichier features/cart/Cart.js
:
import { useStore } from "react-redux";
import { SuperCremeux } from "./models";
import { useEffect, useState } from "react";
export const Cart = () => {
const store = useStore();
const [list, setList] = useState(store.getState().list)
useEffect(() => {
store.subscribe(() => setList(store.getState().list))
})
return <div className="Selection">
<h1>Choisir son menu</h1>
<div className="CartNavBar">
<button onClick={() => store.dispatch({type: 'ADD_PRODUCT', payload: SuperCremeux})}>Ajouter un super crémeux</button>
</div>
{
list.map((item, index) => <span key={index} className="SelectedProduct">{item.title} {item.price} €</span>
)
}
</div>
};
Débuggez votre configuration Redux
Avant d’aller plus loin, je vous propose de vous mettre dans les meilleures conditions pour travailler avec Redux et Redux ToolKit.
En tant qu’utilisateur régulier de React, vous avez sans doute eu l’opportunité d’installer les DevTools, ces outils qui permettent de visualiser et de débugger votre application React dans la barre d’inspection du navigateur.
Eh bien Redux fournit ce même genre d’outil, Redux DevTools !
Découvrons l’outil Redux DevTools :
Nous avons vu que cet outil nous sera utile pour débugger notre application Redux, notamment grâce à la possibilité de suivre les changements du state, de visualiser les actions et leur contenu, la possibilité de rejouer, d’éditer des actions et encore plein d’autres fonctionnalités.
Vous savez à présent comment l’installer et l’utiliser. Il vous sera très utile pour déboguer votre application.
À vous de jouer
Nous voilà maintenant capables de modifier les valeurs de notre store via notre composant. Je vous propose un petit exercice pour continuer à mettre en pratique ce que nous savons faire.
Pour cela, nous allons afficher le montant total de la commande dans notre composant Cart.js
et remplir le besoin suivant :
En tant qu’utilisateur,
tant qu’il n’y a pas de produit dans ma sélection, je peux voir indiquée la mention : “Aucun produit sélectionné pour le moment” ;
lorsque le nombre de produits est supérieur à 0, je peux voir indiquée la somme des prix des produits sélectionnés sous la forme : “Total commande X euros”.
Pour tester que l’on affiche bien le message “Aucun produit sélectionné pour le moment”, n’oubliez pas de retirer les produits par défaut du store.
Une fois que vous avez ajouté tout ça, suivez-moi dans le screencast ci-dessous pour voir la correction :
En résumé
Pour utiliser Redux dans React, il nous faut utiliser
useStore
de react-redux.Nous pouvons utiliser les mêmes outils, le
dispatch
et lesubscribe
, pour écouter et modifier les valeurs du store.Redux peut être utilisé avec des outils qui facilitent le développement, Redux DevTools.
Nous avons déjà créé un composant qui permet l’utilisation de notre state managé. Allons plus loin, avec d’autres composants et en utilisant plus d’outils fournis par Redux Toolkit et react-redux.