Vous avez un projet React avec TypeScript dans frontendBlog, incluant :
Une liste d’articles affichée via le composant Article.
Un formulaire ArticleForm pour ajouter des articles.
Un style Bootstrap appliqué.
Nous allons ajouter :
Un bouton "Supprimer" dans Article avec une prop onDelete.
Un formulaire d’édition pour mettre à jour un article.
Une structure pour une future connexion au backend (simulée pour l’instant).
Étape 1 : Ajouter un bouton "Supprimer" dans Article
Objectif : Permettre de supprimer un article en cliquant sur un bouton.
Modifier Article.tsx :
Ouvre src/components/Article.tsx et mets à jour le code :
tsx
import React from 'react';
interface ArticleProps {
id: number;
title: string;
content: string;
onDelete: (id: number) => void; // Nouvelle prop pour supprimer
}
const Article: React.FC<ArticleProps> = ({ id, title, content, onDelete }) => {
return (
<div className="card mb-3">
<div className="card-body">
<h2 className="card-title">{title}</h2>
<p className="card-text">{content}</p>
<button
className="btn btn-danger"
onClick={() => onDelete(id)} // Appelle onDelete avec l'ID
>
Supprimer
</button>
</div>
</div>
);
};
export default Article;
Explication :
onDelete: (id: number) => void est une nouvelle prop qui prend un id et ne retourne rien (void).
Le bouton <button> utilise Bootstrap (btn btn-danger pour un style rouge) et appelle onDelete(id) quand on clique dessus avec une fonction fléchée pour passer l’id.
onClick={() => onDelete(id)} évite d’exécuter onDelete immédiatement (problème courant avec les événements).
Mettre à jour App.tsx pour gérer la suppression :
Ouvre src/App.tsx et modifie :
tsx
import React, { useState } from 'react';
import './App.css';
import Article from './components/Article';
import ArticleForm from './components/ArticleForm';
function App() {
const [articles, setArticles] = useState([
{ id: 1, title: "Premier article", content: "Ceci est le contenu du premier article." },
{ id: 2, title: "Deuxième article", content: "Ceci est le contenu du deuxième article." },
]);
const addArticle = (title: string, content: string) => {
const newArticle = {
id: Date.now(), // Utilise l'horodatage comme ID temporaire
title,
content,
};
setArticles([...articles, newArticle]);
};
const deleteArticle = (id: number) => {
setArticles(articles.filter((article) => article.id !== id)); // Supprime l'article avec l'ID donné
};
return (
<div className="App container mt-4">
<h1 className="text-center mb-4">Bienvenue sur mon Blog</h1>
<ArticleForm onAddArticle={addArticle} />
<div className="mt-4">
{articles.map((article) => (
<Article
key={article.id}
id={article.id}
title={article.title}
content={article.content}
onDelete={deleteArticle} // Passe la fonction de suppression
/>
))}
</div>
</div>
);
}
export default App;
Explication :
deleteArticle utilise filter pour créer une nouvelle liste sans l’article dont l’id correspond.
onDelete={deleteArticle} passe la fonction au composant Article.
setArticles met à jour l’état avec la nouvelle liste.
Vérifier :
Sauvegarde, lance npm start si nécessaire, et va sur http://localhost:3000.
Clique sur "Supprimer" pour un article : il devrait disparaître.
Étape 2 : Ajouter un formulaire d’édition pour mettre à jour un article
Objectif : Permettre de modifier un article existant.
Créer un composant EditArticleForm :
Dans src/components, crée EditArticleForm.tsx et ajoute :
tsx
import React, { useState } from 'react';
interface EditArticleFormProps {
article: { id: number; title: string; content: string };
onSave: (id: number, title: string, content: string) => void;
onCancel: () => void;
}
const EditArticleForm: React.FC<EditArticleFormProps> = ({ article, onSave, onCancel }) => {
const [title, setTitle] = useState(article.title);
const [content, setContent] = useState(article.content);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (title && content) {
onSave(article.id, title, content);
}
};
return (
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label className="form-label">Titre :</label>
<input
type="text"
className="form-control"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
<div className="mb-3">
<label className="form-label">Contenu :</label>
<textarea
className="form-control"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
</div>
<button type="submit" className="btn btn-primary me-2">
Sauvegarder
</button>
<button type="button" className="btn btn-secondary" onClick={onCancel}>
Annuler
</button>
</form>
);
};
export default EditArticleForm;
Explication :
article est une prop avec les données actuelles.
onSave et onCancel sont des fonctions passées par le parent.
Le formulaire utilise Bootstrap (form-control, mb-3) pour un style propre.
Mettre à jour Article.tsx pour inclure un bouton d’édition :
Modifie src/components/Article.tsx :
tsx
import React, { useState } from 'react';
import EditArticleForm from './EditArticleForm';
interface ArticleProps {
id: number;
title: string;
content: string;
onDelete: (id: number) => void;
onEdit: (id: number) => void; // Nouvelle prop pour lancer l'édition
}
const Article: React.FC<ArticleProps> = ({ id, title, content, onDelete, onEdit }) => {
const [isEditing, setIsEditing] = useState(false);
return (
<div className="card mb-3">
<div className="card-body">
{isEditing ? (
<EditArticleForm
article={{ id, title, content }}
onSave={(id, newTitle, newContent) => {
onEdit(id); // Simule la sauvegarde (à implémenter dans App)
setIsEditing(false);
}}
onCancel={() => setIsEditing(false)}
/>
) : (
<>
<h2 className="card-title">{title}</h2>
<p className="card-text">{content}</p>
<button className="btn btn-danger me-2" onClick={() => onDelete(id)}>
Supprimer
</button>
<button className="btn btn-warning" onClick={() => setIsEditing(true)}>
Modifier
</button>
</>
)}
</div>
</div>
);
};
export default Article;
Explication :
useState(false) gère l’état d’édition.
Si isEditing est true, affiche EditArticleForm; sinon, affiche les données et les boutons.
onEdit est une nouvelle prop (à définir dans App).
Mettre à jour App.tsx pour gérer l’édition :
Modifie src/App.tsx :
tsx
import React, { useState } from 'react';
import './App.css';
import Article from './components/Article';
import ArticleForm from './components/ArticleForm';
function App() {
const [articles, setArticles] = useState([
{ id: 1, title: "Premier article", content: "Ceci est le contenu du premier article." },
{ id: 2, title: "Deuxième article", content: "Ceci est le contenu du deuxième article." },
]);
const addArticle = (title: string, content: string) => {
const newArticle = {
id: Date.now(),
title,
content,
};
setArticles([...articles, newArticle]);
};
const deleteArticle = (id: number) => {
setArticles(articles.filter((article) => article.id !== id));
};
const editArticle = (id: number, newTitle: string, newContent: string) => {
setArticles(
articles.map((article) =>
article.id === id ? { ...article, title: newTitle, content: newContent } : article
)
);
};
return (
<div className="App container mt-4">
<h1 className="text-center mb-4">Bienvenue sur mon Blog</h1>
<ArticleForm onAddArticle={addArticle} />
<div className="mt-4">
{articles.map((article) => (
<Article
key={article.id}
id={article.id}
title={article.title}
content={article.content}
onDelete={deleteArticle}
onEdit={(id) => editArticle(id, articles.find((a) => a.id === id)!.title, articles.find((a) => a.id === id)!.content)} // À améliorer
/>
))}
</div>
</div>
);
}
export default App;
Explication :
editArticle met à jour l’article avec map, remplaçant l’article correspondant par une nouvelle version.
onEdit est passé avec une fonction qui appelle editArticle. (Note : La logique actuelle est basique ; nous l’améliorerons plus tard avec un état d’édition propre.)
L’appel à find est temporaire ; il faudra passer les nouvelles valeurs du formulaire.
Vérifier :
Sauvegarde et actualise.
Clique sur "Modifier", édite les champs, et clique sur "Sauvegarder" : l’article devrait être mis à jour.
Étape 3 : Préparer la connexion au backend (simulation)
Objectif : Mettre en place une structure pour une API REST future.
Simuler une API avec un état local :
Pour l’instant, articles est géré localement. Quand vous serez prêt pour Spring Boot, remplacez useState par des appels fetch ou axios.
Exemple futur avec fetch (à implémenter plus tard) :
Dans App.tsx, tu pourrais remplacer useState par :
tsx
const [articles, setArticles] = useState([]);
useEffect(() => {
fetch('http://localhost:8080/api/articles')
.then((response) => response.json())
.then((data) => setArticles(data));
}, []);
Explication :
useEffect appelle l’API au montage du composant.
fetch récupère les données du backend.
Cela nécessitera un backend fonctionnel (Spring Boot) et des ajustements (ex. gestion des erreurs).
Préparer addArticle pour l’API :Remplace par (futur) :
tsx
const addArticle = async (title: string, content: string) => {
const response = await fetch('http://localhost:8080/api/articles', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title, content }),
});
const newArticle = await response.json();
setArticles([...articles, newArticle]);
};
Explication : Cela enverra une requête POST au backend.
Vérification globale
Lance npm start et teste :
Ajouter un article via le formulaire.
Supprimer un article avec le bouton "Supprimer".
Modifier un article avec "Modifier" et "Sauvegarder".
Si une erreur survient (ex. “Type error”), vérifie les imports et les types dans TypeScript.