React - Les Hooks : Index Complet

Ils permettent d'utiliser l'état et les fonctionnalités de React dans des composants fonctionnels, rendant le code plus simple et réutilisable.


Organisation des Hooks

Cette section est organisée en plusieurs chapitres spécialisés pour une meilleure compréhension :

📚 Hooks de Base

15.0.4.0 - useState

  • État local dans les composants fonctionnels
  • Gestion des mises à jour d'état
  • Optimisation et bonnes pratiques
  • Exemples pratiques avec objets et tableaux

15.0.4.1 - useEffect

  • Effets de bord et cycle de vie
  • Patterns courants : fetch, timers, événements
  • Dependencies et optimisations
  • Cleanup et gestion des fuites mémoire

15.0.4.2 - useRef

  • Références DOM directes
  • Valeurs mutables sans re-render
  • Cas d'usage avancés : timers, intersection observer
  • Différence avec useState

⚡ Hooks de Performance

15.0.4.3 - useMemo

  • Mémorisation des calculs coûteux
  • Optimisation des objets et tableaux
  • Quand et comment utiliser useMemo
  • Impact sur les performances

15.0.4.4 - useCallback

  • Mémorisation des fonctions
  • Éviter les re-renders inutiles
  • Optimisation des composants enfants
  • Patterns avec les événements

🏗️ Hooks de Gestion d'État

15.0.4.5 - useReducer

  • Gestion d'état complexe
  • Pattern Redux-like intégré
  • Actions et reducers
  • Cas d'usage vs useState

15.0.4.6 - useContext

  • Partage de données entre composants
  • Résoudre le prop drilling
  • Context API et providers
  • Patterns avec plusieurs contextes

🚀 Hooks Avancés

15.0.4.7 - Hooks Avancés

  • useLayoutEffect : synchronisation DOM
  • useId : identifiants uniques
  • useTransition : transitions non-bloquantes
  • useDeferredValue : valeurs différées

🎨 Hooks Personnalisés

15.0.4.8 - Custom Hooks

  • Créer ses propres hooks
  • Réutilisabilité et logique partagée
  • Patterns courants : API, UI, utilitaires
  • Composition et bonnes pratiques

Vue d'Ensemble Rapide

Hooks Natifs React

import { 
  useState,         // État local
  useEffect,        // Effets de bord
  useRef,           // Références DOM/valeurs mutables
  useMemo,          // Mémorisation calculs
  useCallback,      // Mémorisation fonctions
  useReducer,       // État complexe
  useContext,       // Context global
  useLayoutEffect,  // Effets synchrones
  useId,            // Identifiants uniques
  useTransition,    // Transitions UI
  useDeferredValue  // Valeurs différées
} from 'react'

Exemple de Hook Personnalisé

// Hook personnalisé simple
function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue)

  const increment = useCallback(() => setCount(c => c + 1), [])
  const decrement = useCallback(() => setCount(c => c - 1), [])
  const reset = useCallback(() => setCount(initialValue), [initialValue])

  return { count, increment, decrement, reset }
}

// Utilisation
function App() {
  const { count, increment, decrement, reset } = useCounter(10)

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
      <button onClick={reset}>Reset</button>
    </div>
  )
}

Règles des Hooks

⚠️ Règles Importantes

  1. Toujours au niveau racine : jamais dans des boucles, conditions ou fonctions imbriquées
  2. Uniquement dans les composants React ou autres hooks personnalisés
  3. Ordre constant : l'ordre d'appel doit être identique à chaque render
  4. Nommage : les hooks personnalisés commencent par "use"
// ✅ BON
function GoodComponent() {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('')
  
  useEffect(() => {
    document.title = `${name} - ${count}`
  }, [name, count])
  
  return <div>...</div>
}

// ❌ MAUVAIS
function BadComponent({ condition }) {
  const [count, setCount] = useState(0)
  
  if (condition) {
    const [name, setName] = useState('') // ❌ Hook conditionnel !
    useEffect(() => {}) // ❌ Hook conditionnel !
  }
  
  return <div>...</div>
}

Patterns Courants

Fetch de Données

function useApiData(url) {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false))
  }, [url])

  return { data, loading, error }
}

State Management Local

function useFormState(initialState) {
  const [values, setValues] = useState(initialState)
  const [errors, setErrors] = useState({})

  const setValue = useCallback((name, value) => {
    setValues(prev => ({ ...prev, [name]: value }))
    setErrors(prev => ({ ...prev, [name]: null }))
  }, [])

  const setError = useCallback((name, error) => {
    setErrors(prev => ({ ...prev, [name]: error }))
  }, [])

  return { values, errors, setValue, setError }
}

UI State

function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue)
  const toggle = useCallback(() => setValue(v => !v), [])
  return [value, toggle]
}

function useModal() {
  const [isOpen, setIsOpen] = useState(false)
  const open = useCallback(() => setIsOpen(true), [])
  const close = useCallback(() => setIsOpen(false), [])
  return { isOpen, open, close }
}

Migration Class → Hooks

Avant (Class Component)

class Counter extends Component {
  constructor(props) {
    super(props)
    this.state = { count: 0 }
  }

  componentDidMount() {
    document.title = `Count: ${this.state.count}`
  }

  componentDidUpdate() {
    document.title = `Count: ${this.state.count}`
  }

  increment = () => {
    this.setState(prev => ({ count: prev.count + 1 }))
  }

  render() {
    return (
      <div>
        <p>{this.state.count}</p>
        <button onClick={this.increment}>+1</button>
      </div>
    )
  }
}

Après (Hooks)

function Counter() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    document.title = `Count: ${count}`
  }, [count])

  const increment = useCallback(() => {
    setCount(prev => prev + 1)
  }, [])

  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>+1</button>
    </div>
  )
}

Ressources Complémentaires