Retour

⚛️ React avec Astro & Tailwind

React - La Bibliothèque UI de Meta

React est une bibliothèque JavaScript créée par Facebook pour construire des interfaces utilisateur dynamiques et réactives. Avec Astro pour la performance et Tailwind CSS pour le design, c'est un combo ultra-puissant ! 🚀

🪝 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 infini
align: 'start' → Alignement
slidesToScroll: 1 → Slides à défiler
skipSnaps: 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

Glisser pour continuer vers SQL Pratique
⬇️