OpenClassrooms devient une université américaine accréditée.
Découvrez ce que cela change pour vousTable des matières
- Partie 1
Initiez-vous à Redux
- Partie 2
Mettez en place Redux dans une application React
- Partie 3
Maîtrisez les bases de Redux Toolkit
- Partie 4
Consommez des appels API via le store
Configurez vos reducers avec les EntityAdapters
#Comprenez ce qu’est un EntityAdapter
Nous faisons bien souvent des applications avec la même approche, et des fonctionnalités qui se rapprochent bien plus qu’il ne nous paraît.
Si nous stockons une liste de “choses”, il est évident qu’une des fonctionnalités sera d’ajouter une “chose” à cette liste. De plus, si nous stockons une liste, nous souhaitons généralement l’afficher.
Dans notre cas, imaginons qu’un client ayant des allergies alimentaires veuille pouvoir indiquer les aliments à retirer de son burger. Pour ce faire, nous pouvons ajouter la fonctionnalité d’ajout de notes qui seront affichées aux cuisiniers de notre restaurant.
Nous pourrions ajouter une slice, des sélecteurs… ou utiliser un outil qui nous permettrait de simplifier la configuration !
Vous voyez où je veux en venir ? Je parle bien du CRUD :
Create (créer),
Read (lire),
Update (mettre à jour),
Delete (supprimer).
Ce sont des opérations que nous pouvons qualifier de génériques. C’est-à-dire que ce sont des actions qui ont des comportements identiques mais qui sont appliquées à des objets différents.
Je vous mets un petit exemple dans le code suivant :
const houses = [
{
id: 1,
name: 'house1',
price: 100000
},
{
id: 2,
name: 'house2',
price: 200000
},
{
id: 3,
name: 'house3',
price: 300000
}
]
const students = [
{
id: 1,
name: 'student1',
average: 20
},
{
id: 2,
name: 'student2',
average: 18
},
{
id: 3,
name: 'student3',
average: 19
}
]
const getHouses = () => {
return houses
}
const getStudents = () => {
return students
}
const getHouseById = (id) => {
return houses.filter(house => house.id === id)
}
const getStudentById = (id) => {
return students.filter(student => student.id === id)
}
const addHouse = (house) => {
houses.push(house)
}
const addStudent = (student) => {
students.push(student)
}
Ci-dessus, students et houses sont des listes d’objets différents. À première vue, on ne peut pas avoir les mêmes actions. Mais en prenant un peu de hauteur, addStudent et addHouse font la même chose, ajouter un élément dans la liste : students pour addStudent et houses pour addHouse .
On peut à priori simplifier tout ça.
Je vous propose la petite implémentation ci-après :
const entityAdapter = () => {
const entities = [];
return {
addOne: (entity) => {
entities.push(entity)
return this
},
selectAll: () => entities,
selectById: (id) => entities.find(entity => entity.id === id),
}
}
const students = entityAdapter()
const houses = entityAdapter()
Nos deux constantes students et houses ont maintenant des actions associées ( addOne , selectAll etselectById ). Plus besoin de les créer une à une, grâce à un outil inclus dans Redux Toolkit : EntityAdapter .
Cet outil permet de configurer notre store et de générer les outils qui vont nous permettre d’effectuer simplement ces opérations CRUD.
L’ EntityAdapter est une fonction qui génère un ensemble de reducers et de sélecteurs destiné à effectuer des opérations CRUD. Avec cet outil, nous aurons accès à des méthodes comme addOne pour ajouter une entrée, addMany pour ajouter plusieurs entrées, setOne pour modifier une entrée, etc.
#Créez un EntityAdapter
Pour utiliser cette fonctionnalité, utilisons createEntityAdapter de Redux Toolkit.
Cette fonction s’emploie un peu comme dans notre implémentation simplifiée vue dans le paragraphe juste au-dessus, avec la particularité de pouvoir surcharger chaque action du CRUD. Pour ce faire, on peut passer en paramètre un objet qui reprend chaque action devant être adaptée. Chaque action peut ainsi être adaptée en cas de besoin.
Par exemple, admettons que l’ id de nos élèves ne soit pas stocké dans student.id mais dans student.studentId . Notre précédente implémentation ne fonctionnerait pas pour students .
En effet, cette fonction ne retournera aucun résultat, car entity.id sera toujours undefined :
selectById: (id) => entities.find(entity => entity.id === id),
Il nous faut donc un moyen d’adapter notre selectById selon l’entité, mais uniquement pour cette fonction.
Avec createEntityAdapter , nous n’avons qu’à ajouter la fonction qui va être appliquée au moment de faire student.selectById , en passant un objet comme ceci :
{
selectById: (students, id) => students.find(student => student.studentId === id),
}
Ce qui nous donne le code suivant :
import { createEntityAdapter } from '@reduxjs/toolkit'
const students = createEntityAdapter({
selectById: (students, id) => students.find(student => student.studentId === id),
})
createEntityAdapter va donc générer un objet, students , avec des actions prédéfinies.
Notre fonction selectById sera exécutée lorsque nous ferons appel à students.selectById et nous aurons bien un résultat pour un id donné.
Pour ajouter notre entityAdaptor à notre store, rien de plus simple. Comme je vous l’ai dit, il génère notre reducer et nos sélecteurs. Ci-dessous, je vous montre comment l’ajouter au store et comment extraire les sélecteurs :
const students = createEntityAdapter({
selectById: (students, id) => students.find(student => student.studentId === id),
})
const studentsSlice = createSlice({
...,
reducers: {
add: students.addOne,
}
})
const store = configureStore({
reducer: {
students: studentsSlice.reducer,
},
})
const studentsSelectors = students.getSelectors(state => state.students)
#À vous de jouer
C’est maintenant l’occasion d’ajouter une fonctionnalité à notre store en employant EntityAdapter .
Je vous propose de reprendre ma proposition qui est de permettre au client d’ajouter des notes pour qu’en cuisine, les préparateurs puissent suivre ces notes et faire des burgers sur mesure.
Qui ne connaît pas un proche ne mangeant pas d’oignons ?
Pour cela, je vous propose d’ajouter un dossier features/notes , un composant features/notes/Notes.js et une slice features/notesSlices.js .
Ajoutez le composant features/notes/Notes.js dans notre fichier app/App.js .
Puis implémentez notre fonctionnalité avec les users stories suivantes :
En tant que client, je peux ajouter une note en remplissant un champ sur plusieurs lignes. En cliquant sur le bouton “Ajouter une note”, la note est sauvegardée.
En tant que client, je peux lister les notes que j’ai saisies. Un bouton “Supprimer” à droite de chaque note permet de retirer la note correspondante.
Une fois que vous avez implémenté tout cela, suivez-moi dans la solution :
#En résumé
L'
EntityAdapterest conçu pour gérer les opérations CRUD (Create, Read, Update, Delete) de manière générique.createEntityAdapterde Redux Toolkit permet de configurer facilement un adaptateur pour n'importe quel type d'entité et offre la flexibilité de surcharger ou d'adapter les actions CRUD selon les besoins spécifiques de chaque entité.Utiliser les
EntityAdapterpermet d’implémenter avec peu d’efforts nos nouvelles fonctionnalités.
Nous voilà outillés pour créer de nouvelles fonctionnalités à la chaîne. Nous maîtrisons les EntityAdapter , nos slices fonctionnent à merveille.
Mais une chose vous turlupine sans doute. JavaScript est un langage qui s’exécute de façon asynchrone dans la plupart des cas, les appels réseaux par exemple. Jusqu’ici, nous n’avons pas abordé cet angle. Eh bien dans la partie suivante, nous allons nous intéresser à l’asynchronisme dans notre store. Prêt pour voyager dans le temps ? 😂
- Formations jusqu’à 100 % financées
- Date de début flexible
- Projets professionnalisants
- Mentorat individuel