States et formulaires
Objectifs
- Afficher une liste de tâches depuis un array
- Utiliser des states pour gérer les tâches
- Comprendre les formulaires avec react et ajouter des tâches
Créer des fausses données
Maintenant que vous avez vu les bases de react, vous allez créer des fausses données pour les tâches. Vous allez créer un array de tâches, et vous allez l'afficher dans un composant. Voilà la liste des tâches, que vous pouvez copier dans un fichier tasks.js :
export const tasks = [
{
id: 1,
title: 'Tâche 1',
description: 'Description de la tâche 1',
status: 'todo'
},
{
id: 2,
title: 'Tâche 2',
description: 'Description de la tâche 2',
status: 'in_progress'
},
{
id: 4,
title: 'Tâche 4',
description: 'Description de la tâche 4',
status: 'todo'
},
{
id: 3,
title: 'Tâche 3',
description: 'Description de la tâche 3',
status: 'done'
}
];
Oui, elles ne sont pas ordonnées, c'est volontaire pour vous apprendre à trier les tâches par statut. Reprenons le code de votre application, et ajoutons un state pour les tâches :
import { useState } from 'react';
import { tasks as importedTasks } from './tasks';
export default function App() {
// On initialise le state avec les fausses tâches de notre fichier tasks.js
const [tasks, setTasks] = useState(importedTasks);
// reste du code...
}
Ok, cool, mais comment on fait pour afficher les tâches dans la bonne liste ? Il faut trier les tâches par statut, puis afficher chaque liste de statut.
// imports...
export default function App() {
const [tasks, setTasks] = useState(importedTasks);
const sortedTasks = {};
tasks.forEach((task) => {
if (sortedTasks[task.status]) {
sortedTasks[task.status].push(task);
} else {
sortedTasks[task.status] = [task];
}
})
console.log(sortedTasks);
return (
// ...
)
}
Vous devriez maintenant voir dans la console la liste des tâches triées par statut. On fait de cette manière afin que, si l'utilisateur le veuille, il puisse rajouter un nouveau statut par exemple "in_review". (Dans une app en production)
Afficher les colonnes & tâches
Du coup, on va afficher nos colonnes de statut et leur tâches, de manière dynamique. Donc, si tasks change, tout se mettra à jour et surtout, si un nouveau statut est ajouté, il s'ajoutera tout seul.
Pour afficher nos colonnes automatiquement, on va utiliser Object.entries pour récupérer le status et les tâches qui vont avec, puis les afficher avec React. Pour afficher automatiquement les colonnes, on va utiliser la méthode map. Ça nous permet de faire un forEach pour chaque élément de l'array, et de renvoyer un élément HTML pour chaque élément de l'array. En gros, on va assigner un élément HTML pour chaque élément de l'array. Voici le code :
return (
// ...
<div>
{Object.entries(sortedTasks).map(([status, tasks]) => {
return (
<div key={status}>
<h3>{status}</h3>
{tasks.map((task) => (
<div key={task.id}>
<h3>{task.title}</h3>
<p>{task.description}</p>
</div>
))}
</div>
);
})}
</div>
// ...
);
Nos colonnes s'affichent automatiquement, ainsi que nos tâches !
Les composants
Maintenant que nous avons nos colonnes et nos tâches, nous allons créer un composant pour afficher chaque colonne et ses tâches. Pour cela, nous allons créer un composant Column et un composant Task.
Un composant, c'est une fonction qui renvoie de l'HTML. Ça nous permet d'isoler l'HTML, le CSS et le JS lié à un élément spécifique (Task par exemple).
export function Column({ status, tasks }) {
return (
<div>
<h3>{status}</h3>
{tasks.map((task) => (
<div key={task.id}>
<h3>{task.title}</h3>
<p>{task.description}</p>
</div>
))}
</div>
);
}
export function Task({ task }) {
return (
<div>
<h3>{task.title}</h3>
<p>{task.description}</p>
</div>
);
}
Les props
En haut des deux composants, vous voyez qu'on récupère des "props", des variables. En fait, ce sont les variables que nous avons passées au composant, comme vous le feriez pour une fonction classique. Donc par exemple, dans le composant Column, on récupère le status et les tâches. Et puis, on passe ces variables de cette manière :
import { Column } from './Column';
return (
<div>
{Object.entries(sortedTasks).map(([status, tasks]) => {
return <Column key={status} status={status} tasks={tasks} />;
})}
</div>
);
Et par exemple, dans le composant Task, on récupère la tâche. On lui passe la tâche en Prop, dans Column :
import { Task } from './Task';
export function Column({ status, tasks }) {
return (
<div>
<h3>{status}</h3>
{tasks.map((task) => (
<div key={task.id}>
<h3>{task.title}</h3>
<p>{task.description}</p>
</div>
))}
</div>
);
}
Formulaires et ajout
On veut ajouter une tâche dans la liste. Pour ça, on va utiliser un formulaire, exactement comme on pourrait le faire en HTML classique :
export default function App() {
// ...code précédent
const handleSubmit = (event) => {
event.preventDefault();
const formData = new FormData(event.target);
const form = Object.fromEntries(formData.entries());
setTasks((prevTasks) => {
return [
...prevTasks,
{
id: prevTasks.length + 1,
title: form.title,
description: form.description,
status: form.status
}
];
});
};
return (
// ...code précédent
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Titre" name="title" />
<input type="text" placeholder="Description" name="description" />
<select name="status" id="status">
<option value="todo">Todo</option>
<option value="in_progress">En cours</option>
<option value="done">Terminé</option>
</select>
<button type="submit">Ajouter</button>
</form>
);
}
Composant Modal
Créer un composant Modal, c'est plutôt simple. Mais, on va apprendre une nouvelle chose c'est les children. Les children c'est les éléments qui sont passés à l'intérieur du composant. Par exemple :
<Modal>
<p>Ça c'est le `children` du composant Modal.</p>
</Modal>
Et on utlisera ça comme ça, pour obtenir un modal où on y choisira le contenu :
export function Modal({ children, isOpen }) {
if (!isOpen) return null;
return (
<div className="fixed inset-0 flex items-center justify-center bg-black/50">
<div className="rounded-md bg-white p-4">{children}</div>
</div>
);
}
Le prop children se passe automatiquement, et le prop isOpen est un boolean qui permet de contrôler l'ouverture du modal. Si isOpen est false, le modal ne s'affiche pas. On va donc pouvoir créer un state pour contrôler l'ouverture du modal dans le App.jsx :
// imports...
export default function App() {
// ...code précédent
const [isOpen, setIsOpen] = useState(false);
// ...code
return (
// ...code précédent
<button onClick={() => setIsOpen(true)}>Ouvrir le modal</button>
<Modal isOpen={isOpen}>
<form onSubmit={handleSubmit}>{/* Le formulaire ici */}</form>
</Modal>
);
}
Bonus
- Faire en sorte que le Modal puisse mettre à jour une tâche
- Pouvoir supprimer une tâche