Contactez-nous

Typer le Context (`createContext`)

Apprenez à utiliser TypeScript pour définir et consommer de manière sûre l'API Context de React, en typant la valeur du contexte avec createContext et useContext.

Sécuriser le partage d'état avec l'API Context

L'API Context de React est un outil puissant pour partager des données ou des états à travers une arborescence de composants sans avoir à passer manuellement les props à chaque niveau (prop drilling). Cependant, en JavaScript simple, rien ne garantit que la valeur fournie par le `Provider` correspond à ce que les composants consommateurs attendent, ni même que le contexte existe bien au-dessus du consommateur.

TypeScript permet de sécuriser l'utilisation de l'API Context en définissant explicitement le type de la valeur qui sera partagée. Cela garantit que le `Provider` fournit une valeur de la bonne forme et que les consommateurs accèdent aux propriétés du contexte en toute sécurité, avec l'aide de l'autocomplétion et de la vérification statique.

Définir la forme de la valeur du Contexte

Comme pour les props, la première étape consiste à définir la structure (la "forme") de la valeur que votre contexte va contenir. Cette valeur peut être un simple type primitif, mais elle est souvent un objet contenant plusieurs états et/ou fonctions de mise à jour. On utilise généralement une `interface` ou un `type` alias pour cela.

// Exemple pour un contexte d'authentification
interface AuthContextType {
  isAuthenticated: boolean;
  user: { id: string; name: string; } | null;
  login: (user: { id: string; name: string; }) => void;
  logout: () => void;
}

// Exemple pour un contexte de thème
type Theme = 'light' | 'dark';
interface ThemeContextType {
  theme: Theme;
  toggleTheme: () => void;
}

Créer le Contexte typé avec `createContext`

Une fois le type de la valeur défini, vous créez le contexte en utilisant `React.createContext()` et en lui passant le type comme paramètre générique : `createContext(defaultValue)`.

La question de la valeur par défaut (`defaultValue`) est importante :

  • Fournir une valeur par défaut significative : Si vous pouvez fournir une valeur par défaut qui correspond au type défini (par exemple, pour un thème, `createContext({ theme: 'light', toggleTheme: () => {} })`), c'est souvent le plus simple. Le consommateur aura toujours une valeur valide, même s'il est utilisé accidentellement sans Provider.
  • Utiliser `null` ou `undefined` comme défaut (plus courant) : Souvent, il n'est pas possible ou logique de fournir une valeur par défaut complète (surtout si le contexte inclut des fonctions dépendant de l'état du Provider). Dans ce cas, on initialise souvent avec `null` ou `undefined`, et on adapte le type du contexte en conséquence (ex: `AuthContextType | null`).
    import React, { createContext } from 'react';
    
    // Type défini précédemment
    interface AuthContextType { /* ... */ }
    
    // Créer le contexte avec une valeur par défaut 'undefined'
    // Le type du contexte devient AuthContextType | undefined
    const AuthContext = createContext(undefined);
    
    export default AuthContext;

L'utilisation de `undefined` comme valeur par défaut est une pratique courante car elle permet de détecter plus facilement si un consommateur est utilisé en dehors d'un Provider correspondant (voir section sur la consommation).

Typer le composant Provider

Le composant qui utilise le `Context.Provider` doit s'assurer que la prop `value` qu'il passe correspond exactement au type défini pour le contexte (ou à la partie non-`undefined` si vous avez utilisé `undefined` comme défaut).

import React, { useState, useMemo, ReactNode } from 'react';
import AuthContext from './AuthContext'; // Le contexte créé précédemment

interface AuthProviderProps {
  children: ReactNode;
}

interface User {
  id: string;
  name: string;
}

export const AuthProvider = ({ children }: AuthProviderProps) => {
  const [user, setUser] = useState(null);

  const login = (userData: User) => {
    setUser(userData);
  };

  const logout = () => {
    setUser(null);
  };

  // Créer l'objet 'value' qui correspond à AuthContextType
  // Utiliser useMemo pour éviter de recréer l'objet à chaque rendu
  const contextValue = useMemo(() => ({
    isAuthenticated: !!user,
    user,
    login,
    logout,
  }), [user]); // Dépendance: user

  return (
    // Passer la valeur typée au Provider
    
      {children}
    
  );
};

TypeScript vérifiera ici que `contextValue` correspond bien à la structure attendue par `AuthContextType`.

Consommer le Contexte typé avec `useContext`

La magie opère lors de la consommation avec le hook `useContext`. En passant le contexte typé (`AuthContext`) à `useContext`, TypeScript infère automatiquement le type de la valeur retournée.

Si vous avez utilisé `undefined` comme valeur par défaut pour `createContext`, le type retourné par `useContext` sera `AuthContextType | undefined`. Il est essentiel de vérifier que la valeur n'est pas `undefined` avant de l'utiliser, pour garantir que le composant est bien utilisé à l'intérieur du Provider correspondant. Une pratique courante est de lever une erreur si le contexte est `undefined`.

import React, { useContext } from 'react';
import AuthContext from './AuthContext';

function UserDisplay() {
  // TypeScript infère que 'auth' est de type AuthContextType | undefined
  const auth = useContext(AuthContext);

  // Vérification obligatoire si le contexte peut être undefined
  if (auth === undefined) {
    throw new Error('UserDisplay doit être utilisé à l\'intérieur d\'un AuthProvider');
  }

  // A partir d'ici, TS sait que 'auth' est de type AuthContextType
  const { isAuthenticated, user, logout } = auth;

  return (
    
{isAuthenticated && user ? ( <>

Bienvenue, {user.name}!

) : (

Veuillez vous connecter.

)}
); } export default UserDisplay;

Pattern recommandé : Hook Consommateur Personnalisé

Pour éviter de répéter la vérification `undefined` dans chaque composant consommateur, il est fortement recommandé de créer un hook personnalisé qui encapsule `useContext` et la vérification d'erreur.

// Dans AuthContext.js ou un fichier séparé
import React, { useContext } from 'react';
import AuthContext from './AuthContext'; // Le contexte créé

// Hook personnalisé pour consommer le contexte
export const useAuth = () => {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth doit être utilisé à l\'intérieur d\'un AuthProvider');
  }
  // Retourne le contexte dont le type est maintenant garanti AuthContextType
  return context; 
};

// --- Utilisation dans un composant ---
import { useAuth } from './AuthContext';

function UserDisplaySimplified() {
  // Utilise le hook personnalisé, pas besoin de vérifier undefined ici
  const { isAuthenticated, user, logout } = useAuth(); 

  return (
    
{/* ... même logique de rendu que précédemment ... */}
); }

Ce pattern rend la consommation du contexte plus propre et plus sûre dans les composants.

Conclusion : Un partage d'état robuste et explicite

Typer l'API Context avec TypeScript transforme un mécanisme de partage d'état potentiellement source d'erreurs en une solution robuste et explicite. En définissant clairement la forme de la valeur du contexte avec `interface` ou `type`, en utilisant le générique `createContext`, et en consommant le contexte de manière sûre (idéalement via un hook personnalisé), vous améliorez considérablement la fiabilité, la maintenabilité et l'expérience de développement de vos applications React utilisant le partage d'état via Context.