🪝 Les Hooks React
useState - Gérer l'État
Permet de créer des variables d'état qui déclenchent un re-render quand elles changent.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Incrémenter
</button>
</div>
);
}
💡 Règle d'Or :
Ne JAMAIS modifier le state directement ! Toujours utiliser le setter.
❌ count = count + 1
✅ setCount(count + 1)
useEffect - Effets de Bord
Exécuter du code après le rendu (API calls, subscriptions, timers...).
import { useEffect, useState } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// Fetch data quand userId change
fetch(\`/api/users/\${userId}\`)
.then(res => res.json())
.then(data => setUser(data));
// Cleanup function
return () => {
// Annuler requêtes en cours
};
}, [userId]); // Dépendances
return <div>{user?.name}</div>;
}
⚠️ Tableau de dépendances :
- •
[]→ Exécute une seule fois au mount - •
[userId]→ Exécute quand userId change - • Pas de tableau → Exécute à chaque render ⚠️
useContext - Partager des Données
Éviter le "prop drilling" en partageant des données globalement.
import { createContext, useContext } from 'react';
// Créer le contexte
const ThemeContext = createContext('light');
// Provider
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
// Consommer
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={theme}>Bouton</button>;
}
Autres Hooks Importants
useRef
Référence mutable qui ne déclenche pas de re-render
useMemo
Mémoriser un calcul coûteux
useCallback
Mémoriser une fonction
useReducer
State complexe avec actions
📦 Props & State
Props (Propriétés)
Données passées du parent à l'enfant (lecture seule).
// Parent
<UserCard name="Alice" age={25} />
// Enfant
function UserCard({ name, age }) {
return <div>{name} - {age} ans</div>;
}
State (État)
Données internes au composant (modifiables).
function Form() {
const [email, setEmail] = useState('');
return (
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
);
}
🔑 Différence Clé :
- • Props : viennent du parent, immuables
- • State : géré en interne, modifiable avec setState
🚀 Astro + React = Performance
Pourquoi Astro ?
Astro charge ZÉRO JavaScript par défaut ! Les composants React ne sont hydratés que quand nécessaire → sites ultra-rapides ⚡
astro.config.mjs
export default {
integrations: [react()],
};
Directives client:
client:load
Hydrate immédiatement
client:visible
Hydrate quand visible (lazy loading)
client:idle
Hydrate quand le navigateur est idle
src/components/Counter.jsx
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button
onClick={() => setCount(count + 1)}
className="bg-indigo-600 text-white px-4 md:px-6 py-3 rounded-lg"
>
Clicks: {count}
</button>
);
}
// Dans Astro
---
import Counter from './Counter.jsx';
---
<Counter client:visible />
🎨 Tailwind CSS + React
Classes Dynamiques
function Button({ variant }) {
const baseClasses = "px-4 md:px-6 py-3 rounded-lg font-bold";
const variants = {
primary: "bg-indigo-600 text-white hover:bg-indigo-700",
secondary: "bg-gray-200 text-gray-800 hover:bg-gray-300",
danger: "bg-red-600 text-white hover:bg-red-700"
};
return (
<button className={\`\${baseClasses} \${variants[variant]}\`}>
Clique ici
</button>
);
}
Conditional Classes
function Card({ isActive }) {
return (
<div className={\`
p-4 md:p-6 rounded-xl transition-all
\${isActive ? 'bg-indigo-600 text-white shadow-2xl' : 'bg-white'}
\`}>
{isActive ? '✅ Actif' : '⚪ Inactif'}
</div>
);
}
🎠 Embla Carousel
Installation & Setup
npm install embla-carousel-react
import useEmblaCarousel from 'embla-carousel-react';
function Carousel() {
const [emblaRef] = useEmblaCarousel({ loop: true });
return (
<div className="overflow-hidden" ref={emblaRef}>
<div className="flex">
<div className="flex-[0_0_100%]">Slide 1</div>
<div className="flex-[0_0_100%]">Slide 2</div>
<div className="flex-[0_0_100%]">Slide 3</div>
</div>
</div>
);
}
🎯 Options Utiles
loop: true → Carousel infinialign: 'start' → AlignementslidesToScroll: 1 → Slides à défilerskipSnaps: false → Pas de saut✨ Best Practices React
✅ Faire
- • Utiliser des composants fonctionnels avec hooks
- • Séparer logique et présentation
- • Utiliser TypeScript pour la sécurité des types
- • Mémoriser avec useMemo/useCallback si nécessaire
- • Utiliser des keys uniques pour les listes
❌ Éviter
- • Modifier le state directement
- • Oublier les dépendances dans useEffect
- • Trop de composants dans un seul fichier
- • Utiliser index comme key dans les listes
- • Appeler les hooks conditionnellement
🎯 Exemple Complet : TodoApp
import { useState } from 'react';
export default function TodoApp() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const addTodo = () => {
if (input.trim()) {
setTodos([...todos, { id: Date.now(), text: input, done: false }]);
setInput('');
}
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
));
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div className="max-w-md mx-auto p-4 md:p-6 bg-white rounded-xl shadow-2xl">
<h1 className="text-2xl md:text-3xl font-bold text-indigo-600 mb-6">Todo List</h1>
<div className="flex gap-2 mb-6">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && addTodo()}
className="flex-1 px-4 py-2 border-2 border-indigo-300 rounded-lg"
placeholder="Nouvelle tâche..."
/>
<button
onClick={addTodo}
className="bg-indigo-600 text-white px-4 md:px-6 py-2 rounded-lg hover:bg-indigo-700"
>
Ajouter
</button>
</div>
<ul className="space-y-2">
{todos.map(todo => (
<li
key={todo.id}
className="flex items-center gap-3 p-4 bg-gray-50 rounded-lg"
>
<input
type="checkbox"
checked={todo.done}
onChange={() => toggleTodo(todo.id)}
className="w-5 h-5"
/>
<span className={\`flex-1 \${todo.done ? 'line-through text-gray-400' : ''}\`}>
{todo.text}
</span>
<button
onClick={() => deleteTodo(todo.id)}
className="text-red-600 hover:text-red-800"
>
🗑️
</button>
</li>
))}
</ul>
</div>
);
}
🎮 Quiz Interactif !
Réponds aux questions et apprends en t'amusant !
💡 Feedback immédiat après chaque réponse