
Typer le state (`useState`)
Apprenez à utiliser TypeScript pour typer efficacement l'état local géré par le hook `useState` dans vos composants React, en utilisant l'inférence de type ou la généricité explicite.
Assurer la cohérence de l'état local
Le hook `useState` est le mécanisme de base pour introduire un état local dans les composants fonctionnels React. Tout comme pour les props, il est crucial de s'assurer que cet état est utilisé de manière cohérente et que son type ne change pas de manière inattendue au fil du temps. Tenter d'assigner une valeur d'un type incorrect à une variable d'état peut entraîner des erreurs subtiles et des comportements imprévisibles.
TypeScript nous permet de définir explicitement le type de la variable d'état gérée par `useState`. Cela garantit que la valeur initiale et toutes les mises à jour ultérieures via la fonction de mise à jour respectent le type défini, offrant ainsi une sécurité et une prévisibilité accrues pour la gestion de l'état local.
Inférence de type avec `useState`
Dans de nombreux cas simples, vous n'avez même pas besoin de spécifier explicitement le type pour `useState`. TypeScript est suffisamment intelligent pour inférer (déduire) le type de l'état à partir de la valeur initiale que vous lui fournissez.
import React, { useState } from 'react';
function Counter() {
// TypeScript infère que 'count' est de type 'number'
// car la valeur initiale (0) est un nombre.
const [count, setCount] = useState(0);
// OK: 1 est un nombre
const handleIncrement = () => setCount(count + 1);
// Erreur TypeScript détectée:
// Argument of type 'string' is not assignable to parameter of type 'number | ((prevState: number) => number)'.
// const makeError = () => setCount("cinq");
return (
Compteur: {count}
);
}
export default Counter;Si la valeur initiale est une chaîne, l'état sera inféré comme `string`. Si c'est un booléen, il sera `boolean`, etc. L'inférence de type fonctionne très bien pour les types primitifs et même pour les objets et tableaux simples lorsque leur structure initiale est claire.
Typage explicite avec les génériques (`useState`)
Il existe des situations où l'inférence de type n'est pas suffisante ou souhaitable :
- Etat initial `null` ou `undefined` : Si l'état peut être initialement `null` mais contiendra plus tard un objet d'un type spécifique (par exemple, des données utilisateur chargées depuis une API).
- Types complexes ou unions : Si l'état peut prendre plusieurs types différents (une union type) ou si sa structure est complexe et non entièrement définie par la valeur initiale.
- Clarté explicite : Parfois, même si l'inférence fonctionnerait, vous préférez rendre le type de l'état explicite pour une meilleure lisibilité ou documentation.
Dans ces cas, vous pouvez spécifier explicitement le type de l'état en utilisant la syntaxe des génériques avec `useState`, en passant le type entre chevrons (`<>`) juste après `useState` : `useState
Exemples de typage explicite
Etat initial `null` ou objet
import React, { useState, useEffect } from 'react';
interface User {
id: number;
name: string;
email: string;
}
function UserProfile({ userId }: { userId: number }) {
// L'état peut être User OU null initialement
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then((data: User) => {
setUser(data); // OK: data est de type User
// setUser("error"); // Erreur TS: "error" n'est ni User ni null
})
.catch(() => setUser(null)) // OK: null est un type autorisé
.finally(() => setLoading(false));
}, [userId]);
if (loading) return Chargement...
;
if (!user) return Utilisateur non trouvé.
;
return (
{user.name}
{/* OK: TS sait que user est de type User ici */}
{user.email}
);
}
export default UserProfile; Etat pouvant être une union de types
import React, { useState } from 'react';
type Status = 'idle' | 'loading' | 'success' | 'error';
function DataFetcher() {
// L'état status peut prendre l'une des 4 chaînes définies dans Status
const [status, setStatus] = useState('idle');
const fetchData = () => {
setStatus('loading'); // OK
// ... logique de fetch ...
// setStatus('pending'); // Erreur TS: 'pending' n'est pas dans le type Status
};
return (
Statut: {status}
);
}
export default DataFetcher; Etat de type tableau
import React, { useState } from 'react';
interface Item {
id: string;
value: string;
}
function TodoList() {
// L'état est un tableau d'objets Item
const [items, setItems] = useState- ([]); // Initialisé à un tableau vide
const addItem = () => {
const newItem: Item = { id: Date.now().toString(), value: 'Nouvelle tâche' };
// OK: on passe un nouveau tableau contenant des Items
setItems(prevItems => [...prevItems, newItem]);
// setItems("une chaine"); // Erreur TS
};
return (
{items.map(item => (
- {item.value}
// OK: TS sait que item est de type Item
))}
);
}
export default TodoList; Conclusion : Sécuriser l'état local
Typer l'état local géré par `useState` est une étape simple mais efficace pour renforcer la robustesse de vos composants React. Que ce soit par l'inférence de type pour les cas simples ou par le typage explicite via les génériques (`useState
Cela prévient les erreurs liées à des types d'état inattendus et rend votre code plus facile à comprendre et à maintenir, car le type de chaque variable d'état est clairement défini.