Installation & Setup
Créer un projet React
npmVite

Vite est l'outil recommandé pour créer un projet React — démarrage rapide, HMR instantané et build optimisé.

Bash
# Vite (recommandé)
npm create vite@latest mon-app -- --template react
cd mon-app && npm install && npm run dev

# Create React App (classique)
npx create-react-app mon-app
cd mon-app && npm start

# Scripts npm
npm run dev       # serveur de développement (Vite)
npm run build     # build de production
npm run preview   # prévisualiser le build
Composants
Composants fonction
JSXexport

Un composant React est une fonction qui retourne du JSX. Il peut être déclaré avec function ou comme une arrow function.

JSX
// Composant simple
function Bonjour() {
  return <h1>Bonjour monde !</h1>;
}

// Arrow function
const Bonjour = () => <h1>Bonjour monde !</h1>;

// Export
export default Bonjour;
export { Bonjour }; // export nommé

// Utilisation
<Bonjour />

// Composant avec plusieurs éléments (Fragment)
function App() {
  return (
    <>
      <Header />
      <Main />
      <Footer />
    </>
  );
}
JSX
Syntaxe JSX
expressionsattributs

JSX est une extension de syntaxe JavaScript. Les expressions JS s'écrivent entre accolades {}. Les attributs suivent la convention camelCase.

JSX
const nom = "Alice";
const estConnecte = true;
const styles = { color: "red", fontSize: "16px" };

// Expressions JS entre accolades {}
<h1>Bonjour {nom} !</h1>
<p>{2 + 2}</p>
<p>{estConnecte ? "Connecté" : "Déconnecté"}</p>

// Attributs JSX (camelCase)
<div className="container">         {/* class → className */}
<label htmlFor="email">Email</label> {/* for → htmlFor */}
<input
  type="text"
  value={valeur}
  onChange={e => setValeur(e.target.value)}
  disabled={!estActif}              {/* true/false */}
  style={{ color: "red", marginTop: "10px" }}
/>

// Auto-fermeture obligatoire pour les balises vides
<br /> <img src="..." alt="..." /> <input />

// Commentaires en JSX
{/* Ceci est un commentaire JSX */}

// Fragments
<React.Fragment key={id}>...</React.Fragment>
<>...</>

// dangerouslySetInnerHTML (attention XSS !)
<div dangerouslySetInnerHTML={{ __html: htmlSanitize }} />
Props
Passer et recevoir des props
propschildren

Les props permettent de passer des données d'un composant parent à un composant enfant. Elles sont en lecture seule.

JSX
// Passer des props
<UserCard nom="Alice" age={30} actif={true} onClick={handleClick} />

// Recevoir des props
function UserCard({ nom, age, actif = false, onClick }) {
  return (
    <div className={`card ${actif ? "active" : ""}`} onClick={onClick}>
      <h2>{nom}</h2>
      <p>{age} ans</p>
    </div>
  );
}

// Prop children
function Modal({ titre, children, onClose }) {
  return (
    <div className="modal">
      <div className="modal-header">
        <h2>{titre}</h2>
        <button onClick={onClose}>✕</button>
      </div>
      <div className="modal-body">{children}</div>
    </div>
  );
}
// Utilisation
<Modal titre="Connexion" onClose={() => setOpen(false)}>
  <LoginForm />
</Modal>

// Spread des props
const props = { nom: "Alice", age: 30 };
<UserCard {...props} />

// PropTypes (validation runtime)
import PropTypes from "prop-types";
UserCard.propTypes = {
  nom: PropTypes.string.isRequired,
  age: PropTypes.number,
  onClick: PropTypes.func,
};
UserCard.defaultProps = { age: 0 };
useState
Gestion de l'état local
hookétat

useState retourne une valeur d'état et une fonction pour la mettre à jour. Chaque appel à la fonction de mise à jour provoque un re-rendu.

JSX
import { useState } from "react";

// Valeur simple
const [count, setCount] = useState(0);
const [nom, setNom]     = useState("");
const [actif, setActif] = useState(false);

// Objet
const [user, setUser] = useState({ nom: "", email: "" });

// Mise à jour
setCount(count + 1);
setCount(prev => prev + 1);   // fonction de mise à jour (sûre)

// Objet : spread pour ne pas perdre les autres propriétés
setUser(prev => ({ ...prev, nom: "Alice" }));

// Tableau
const [items, setItems] = useState([]);
setItems(prev => [...prev, nouvelItem]);           // ajouter
setItems(prev => prev.filter(i => i.id !== id));   // supprimer
setItems(prev => prev.map(i => i.id === id ? { ...i, done: true } : i)); // modifier

// Initialisation lazy (pour calculs coûteux)
const [data, setData] = useState(() => JSON.parse(localStorage.getItem("data")) ?? []);

// Exemple formulaire complet
function LoginForm() {
  const [form, setForm] = useState({ email: "", password: "" });
  const [errors, setErrors] = useState({});

  const handleChange = (e) => {
    setForm(prev => ({ ...prev, [e.target.name]: e.target.value }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(form);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="email"    value={form.email}    onChange={handleChange} />
      <input name="password" value={form.password} onChange={handleChange} type="password" />
      <button type="submit">Connexion</button>
    </form>
  );
}
useEffect
Effets de bord et cycle de vie
hookasync

useEffect permet d'effectuer des opérations après le rendu : fetch de données, abonnements, manipulation du DOM. Le tableau de dépendances contrôle quand l'effet s'exécute.

JSX
import { useEffect } from "react";

// Exécuté après chaque rendu
useEffect(() => {
  console.log("Rendu !");
});

// Exécuté une seule fois (montage)
useEffect(() => {
  fetchData();
}, []);

// Exécuté quand userId change
useEffect(() => {
  fetchUser(userId);
}, [userId]);

// Avec nettoyage (cleanup)
useEffect(() => {
  const timer = setInterval(() => setCount(c => c + 1), 1000);
  return () => clearInterval(timer); // cleanup au démontage ou avant la prochaine exécution
}, []);

// Fetch de données
useEffect(() => {
  const controller = new AbortController();

  async function load() {
    try {
      const res = await fetch(`/api/users/${id}`, { signal: controller.signal });
      const data = await res.json();
      setUser(data);
    } catch (err) {
      if (err.name !== "AbortError") setError(err.message);
    } finally {
      setLoading(false);
    }
  }

  setLoading(true);
  load();
  return () => controller.abort(); // annule si l'id change avant la fin
}, [id]);
useRef
Références DOM et valeurs persistantes
hookDOM

useRef retourne un objet mutable dont la propriété .current persiste entre les rendus sans provoquer de re-rendu lors de sa modification.

JSX
import { useRef } from "react";

// Référence DOM
function InputFocus() {
  const inputRef = useRef(null);

  const focus = () => inputRef.current.focus();

  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={focus}>Focus</button>
    </>
  );
}

// Valeur persistante SANS re-rendu (ex: timer, valeur précédente)
function Chrono() {
  const [time, setTime] = useState(0);
  const timerRef = useRef(null);

  const start = () => {
    timerRef.current = setInterval(() => setTime(t => t + 1), 1000);
  };
  const stop = () => clearInterval(timerRef.current);

  return (
    <>
      <p>{time}s</p>
      <button onClick={start}>Start</button>
      <button onClick={stop}>Stop</button>
    </>
  );
}

// Garder la valeur précédente
function usePrevious(value) {
  const ref = useRef();
  useEffect(() => { ref.current = value; });
  return ref.current;
}
useContext
Partage d'état global sans prop drilling
hookcontext

useContext permet de consommer un contexte React sans avoir à passer des props à chaque niveau de l'arbre de composants.

JSX
import { createContext, useContext, useState } from "react";

// 1. Créer le contexte
const ThemeContext = createContext("light");
const AuthContext  = createContext(null);

// 2. Fournir le contexte
function App() {
  const [theme, setTheme] = useState("dark");
  const [user, setUser]   = useState(null);

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <AuthContext.Provider value={{ user, setUser }}>
        <Router />
      </AuthContext.Provider>
    </ThemeContext.Provider>
  );
}

// 3. Consommer le contexte
function Navbar() {
  const { user } = useContext(AuthContext);
  const { theme, setTheme } = useContext(ThemeContext);

  return (
    <nav className={theme}>
      <span>{user?.nom ?? "Invité"}</span>
      <button onClick={() => setTheme(t => t === "dark" ? "light" : "dark")}>
        Toggle Thème
      </button>
    </nav>
  );
}

// Custom hook pour le contexte
export const useAuth = () => useContext(AuthContext);
export const useTheme = () => useContext(ThemeContext);
useMemo & useCallback
Mémoïsation et optimisation des performances
hookperf

useMemo mémoïse le résultat d'un calcul coûteux. useCallback mémoïse une fonction pour éviter de recréer sa référence à chaque rendu.

JSX
import { useMemo, useCallback } from "react";

// useMemo — mémoïse le RÉSULTAT d'un calcul coûteux
const total = useMemo(() => {
  return items.reduce((sum, item) => sum + item.prix, 0);
}, [items]); // recalcule seulement si items change

// useCallback — mémoïse une FONCTION
const handleDelete = useCallback((id) => {
  setItems(prev => prev.filter(item => item.id !== id));
}, []); // la fonction ne change jamais de référence

// useCallback avec dépendance
const handleSearch = useCallback((query) => {
  return items.filter(item => item.nom.includes(query));
}, [items]);

// Quand utiliser ?
// useMemo   : calculs lourds, filtres/tris sur grandes listes
// useCallback : fonctions passées à des composants enfants avec React.memo
Custom Hooks
Créer ses propres hooks
hookréutilisable

Un custom hook est une fonction dont le nom commence par use et qui peut appeler d'autres hooks. Ils permettent d'extraire et réutiliser de la logique avec état.

JSX
// Hook de fetch générique
function useFetch(url) {
  const [data, setData]       = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError]     = useState(null);

  useEffect(() => {
    setLoading(true);
    fetch(url)
      .then(res => { if (!res.ok) throw new Error(res.statusText); return res.json(); })
      .then(data => { setData(data); setLoading(false); })
      .catch(err => { setError(err.message); setLoading(false); });
  }, [url]);

  return { data, loading, error };
}

// Utilisation
const { data: users, loading, error } = useFetch("/api/users");

// Hook localStorage
function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() =>
    JSON.parse(localStorage.getItem(key)) ?? initialValue
  );
  const set = (val) => {
    setValue(val);
    localStorage.setItem(key, JSON.stringify(val));
  };
  return [value, set];
}

// Hook media query
function useMediaQuery(query) {
  const [matches, setMatches] = useState(() => window.matchMedia(query).matches);
  useEffect(() => {
    const media = window.matchMedia(query);
    const handler = () => setMatches(media.matches);
    media.addEventListener("change", handler);
    return () => media.removeEventListener("change", handler);
  }, [query]);
  return matches;
}
const isMobile = useMediaQuery("(max-width: 768px)");
Formulaires
Formulaire contrôlé avec validation
formulairevalidation

Dans un formulaire contrôlé, React est la source de vérité pour la valeur des champs via value et onChange.

JSX
// Formulaire contrôlé
function RegisterForm() {
  const [form, setForm]     = useState({ nom: "", email: "", password: "" });
  const [errors, setErrors] = useState({});
  const [loading, setLoading] = useState(false);

  const handleChange = (e) =>
    setForm(prev => ({ ...prev, [e.target.name]: e.target.value }));

  const validate = () => {
    const err = {};
    if (!form.nom)      err.nom = "Nom requis";
    if (!form.email.includes("@")) err.email = "Email invalide";
    if (form.password.length < 8)  err.password = "8 caractères minimum";
    return err;
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    const err = validate();
    if (Object.keys(err).length > 0) return setErrors(err);
    setLoading(true);
    try {
      await api("/api/register", { method: "POST", body: form });
    } finally { setLoading(false); }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="nom" value={form.nom} onChange={handleChange} />
      {errors.nom && <span className="error">{errors.nom}</span>}

      <input name="email" type="email" value={form.email} onChange={handleChange} />
      {errors.email && <span className="error">{errors.email}</span>}

      <input name="password" type="password" value={form.password} onChange={handleChange} />
      {errors.password && <span className="error">{errors.password}</span>}

      <button type="submit" disabled={loading}>
        {loading ? "Chargement..." : "S'inscrire"}
      </button>
    </form>
  );
}
Listes & Keys
Rendu de listes avec .map()
mapkey

L'attribut key est obligatoire lors du rendu de listes. Il doit être unique et stable — préférer un identifiant métier plutôt que l'index du tableau.

JSX
// Rendu de liste
const users = [
  { id: 1, nom: "Alice", role: "admin" },
  { id: 2, nom: "Bob",   role: "user" },
];

function UserList({ users }) {
  if (!users.length) return <p>Aucun utilisateur.</p>;

  return (
    <ul>
      {users.map(user => (
        <UserItem key={user.id} user={user} />  // key OBLIGATOIRE, unique, stable
      ))}
    </ul>
  );
}

// Clés : utiliser un id unique, PAS l'index (sauf liste statique)
// ❌  {items.map((item, index) => <li key={index}>)}
// ✅  {items.map(item => <li key={item.id}>)}

// Filtrer et trier
{users
  .filter(u => u.role === "admin")
  .sort((a, b) => a.nom.localeCompare(b.nom))
  .map(u => <UserItem key={u.id} user={u} />)
}
Conditional Rendering
Affichage conditionnel
ternaire&&

React offre plusieurs patterns pour afficher conditionnellement des éléments. Attention au court-circuit && avec les valeurs falsy numériques.

JSX
// if/else avec JSX
function Status({ estConnecte }) {
  if (estConnecte) return <p>Bienvenue !</p>;
  return <p>Veuillez vous connecter.</p>;
}

// Opérateur ternaire
<div>{estConnecte ? <Dashboard /> : <LoginPage />}</div>

// Court-circuit && (attention : ne jamais mettre 0 !)
{isLoading && <Spinner />}
{error && <ErrorMessage message={error} />}
{!!users.length && <UserList users={users} />}  // !! pour éviter le 0

// Rendu null (ne rien afficher)
if (!data) return null;

// État de chargement + erreur + données
function UserPage({ id }) {
  const { data, loading, error } = useFetch(`/api/users/${id}`);

  if (loading) return <div className="spinner">Chargement...</div>;
  if (error)   return <div className="error">Erreur: {error}</div>;
  if (!data)   return null;

  return <UserCard user={data} />;
}
React Router v6
Configuration des routes
routingnpm

React Router v6 utilise <Routes> et <Route> pour définir les routes. Les routes imbriquées s'affichent via <Outlet />.

JSX
import { BrowserRouter, Routes, Route, Link, NavLink, useNavigate, useParams, useSearchParams, Outlet } from "react-router-dom";

// npm install react-router-dom

// Configuration des routes
function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/"          element={<HomePage />} />
        <Route path="/users"     element={<UsersPage />} />
        <Route path="/users/:id" element={<UserDetail />} />
        <Route path="/about"     element={<AboutPage />} />
        <Route path="*"          element={<NotFound />} />  {/* 404 */}

        {/* Routes imbriquées */}
        <Route path="/dashboard" element={<Dashboard />}>
          <Route index            element={<DashboardHome />} />
          <Route path="profil"    element={<Profil />} />
          <Route path="settings"  element={<Settings />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

// Outlet (rendu des routes enfants)
function Dashboard() {
  return (
    <div>
      <Sidebar />
      <main><Outlet /></main>  {/* les routes enfants s'affichent ici */}
    </div>
  );
}

// Navigation
<Link to="/users">Utilisateurs</Link>
<NavLink to="/users" className={({ isActive }) => isActive ? "active" : ""}>
  Utilisateurs
</NavLink>

// Hooks de navigation
function UserDetail() {
  const { id } = useParams();            // paramètres URL (/users/:id)
  const [params] = useSearchParams();    // query string (?page=2)
  const navigate  = useNavigate();       // navigation programmatique

  const page = params.get("page") ?? 1;

  return (
    <div>
      <p>User ID: {id}</p>
      <button onClick={() => navigate("/users")}>Retour</button>
      <button onClick={() => navigate(-1)}>Page précédente</button>
    </div>
  );
}

// Route protégée
function ProtectedRoute({ children }) {
  const { user } = useAuth();
  if (!user) return <Navigate to="/login" replace />;
  return children;
}
// Usage: <Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
Zustand / State global
Gestion d'état global avec Zustand
zustandstore

Zustand est une solution de gestion d'état légère et sans boilerplate. Le store est accessible depuis n'importe quel composant sans Provider.

JSX
// npm install zustand
import { create } from "zustand";

// Créer un store
const useCartStore = create((set, get) => ({
  items: [],
  total: 0,

  addItem: (product) =>
    set(state => ({
      items: [...state.items, product],
      total: state.total + product.prix,
    })),

  removeItem: (id) =>
    set(state => {
      const item = state.items.find(i => i.id === id);
      return {
        items: state.items.filter(i => i.id !== id),
        total: state.total - (item?.prix ?? 0),
      };
    }),

  clearCart: () => set({ items: [], total: 0 }),
  itemCount: () => get().items.length,
}));

// Utilisation dans n'importe quel composant
function CartButton() {
  const { items, addItem } = useCartStore();
  return <button>{items.length} articles</button>;
}

function ProductCard({ product }) {
  const addItem = useCartStore(state => state.addItem);
  return <button onClick={() => addItem(product)}>Ajouter au panier</button>;
}

Aucun résultat pour votre recherche.