Using Generics in Hooks
Generics are a powerful feature in TypeScript that let you write flexible, reusable code while still maintaining type safety. When building custom hooks in React, generics allow you to create hooks that can adapt to different data types without losing the benefits of static type checking. This is especially useful for hooks that fetch data, handle forms, or manage any logic that could work with multiple types.
Suppose you want to build a custom hook for fetching data from an API. If you hard-code the type of data it returns, you limit the hook's usefulness. Instead, you can use a generic type parameter so the hook can work with any data shape, and TypeScript will enforce correct usage.
For example, a generic data fetching hook might look like this:
import { useState, useEffect } from "react";
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(url)
.then((res) => res.json())
.then((json: T) => {
setData(json);
setLoading(false);
});
}, [url]);
return { data, loading };
}
Here, the <T> syntax is used to declare a generic type parameter. When you use this hook, you specify the type of data you expect, and TypeScript will make sure you use it correctly:
type User = {
id: number;
name: string;
};
const { data, loading } = useFetch<User>("/api/user/1");
This pattern ensures that data will always be of type User | null, so you get full type safety across your component.
Another common scenario is form handling. You can create a generic hook that manages form state for any shape of form values:
import { useState } from "react";
function useForm<T>(initialValues: T) {
const [values, setValues] = useState<T>(initialValues);
function handleChange<K extends keyof T>(key: K, value: T[K]) {
setValues((prev) => ({
...prev,
[key]: value,
}));
}
return { values, handleChange };
}
When using this hook, you define the shape of your form values up front:
type LoginForm = {
username: string;
password: string;
};
const { values, handleChange } = useForm<LoginForm>({
username: "",
password: "",
});
This approach gives you strong typing for form values and change handlers, reducing bugs and improving code clarity.
1. What is the benefit of using generics in custom hooks?
2. Which syntax is used to declare a generic type parameter in a hook?
Kiitos palautteestasi!
Kysy tekoälyä
Kysy tekoälyä
Kysy mitä tahansa tai kokeile jotakin ehdotetuista kysymyksistä aloittaaksesi keskustelumme
Can you explain how to use generics with more complex data structures in hooks?
What are some common pitfalls when using generics in custom React hooks?
Can you show an example of a generic hook for handling arrays or lists?
Awesome!
Completion rate improved to 4.17
Using Generics in Hooks
Pyyhkäise näyttääksesi valikon
Generics are a powerful feature in TypeScript that let you write flexible, reusable code while still maintaining type safety. When building custom hooks in React, generics allow you to create hooks that can adapt to different data types without losing the benefits of static type checking. This is especially useful for hooks that fetch data, handle forms, or manage any logic that could work with multiple types.
Suppose you want to build a custom hook for fetching data from an API. If you hard-code the type of data it returns, you limit the hook's usefulness. Instead, you can use a generic type parameter so the hook can work with any data shape, and TypeScript will enforce correct usage.
For example, a generic data fetching hook might look like this:
import { useState, useEffect } from "react";
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(url)
.then((res) => res.json())
.then((json: T) => {
setData(json);
setLoading(false);
});
}, [url]);
return { data, loading };
}
Here, the <T> syntax is used to declare a generic type parameter. When you use this hook, you specify the type of data you expect, and TypeScript will make sure you use it correctly:
type User = {
id: number;
name: string;
};
const { data, loading } = useFetch<User>("/api/user/1");
This pattern ensures that data will always be of type User | null, so you get full type safety across your component.
Another common scenario is form handling. You can create a generic hook that manages form state for any shape of form values:
import { useState } from "react";
function useForm<T>(initialValues: T) {
const [values, setValues] = useState<T>(initialValues);
function handleChange<K extends keyof T>(key: K, value: T[K]) {
setValues((prev) => ({
...prev,
[key]: value,
}));
}
return { values, handleChange };
}
When using this hook, you define the shape of your form values up front:
type LoginForm = {
username: string;
password: string;
};
const { values, handleChange } = useForm<LoginForm>({
username: "",
password: "",
});
This approach gives you strong typing for form values and change handlers, reducing bugs and improving code clarity.
1. What is the benefit of using generics in custom hooks?
2. Which syntax is used to declare a generic type parameter in a hook?
Kiitos palautteestasi!