- Published on
Building Custom React Hooks with TypeScript
In this article, we will explore the process of creating custom React hooks using TypeScript, along with a practical code example.
React hooks were introduced in React version 16.8 they revolutionized the way we write functional components in React. Creating custom Hooks in React allows developers reuse logic across multiple components. If you want to read More info about hooks can be found Here.
Rule of Hooks
There are a few rules to follow to create a custom hooks
- The name should always start with the word
use
to indicate that they are hooks. - Only Call Hooks at the Top Level
- Hooks should always be called at the top level of a functional component or another custom hook.
- Do not call hooks inside loops, conditions, or nested functions.
- Hooks can only be used in functional components, not in class components.
For this article we are going to create a custom hook called useFetchData
that is used to fetch data from API. This hook will return the loading state, the fetched data, and an error message if any.
First, let’s create the file useFetchData.ts
and define the FetchDataReturnType
type, which describes the shape of the values returned by our hook.
type FetchDataReturnType<T> = {
data: T | null
loading: boolean
error: string | null
}
The FetchDataReturnType
type itself is an object type with three properties:
data: T | null
: This property represents the actual data that is returned by the hook and has a type ofT
, which is generic type parameter. TheT | null
union type allows thedata
property to be either of typeT
ornull
.loading: boolean
: This property represents the loading state and has a type ofboolean
. It indicates whether the data is currently being fetched or done fetching.error: string | null
: This property represents the error message and has a type ofstring
ornull
.
Now let's implement the actual logic of our hook
import { useState, useEffect } from 'react'
type FetchDataReturnType<T> = {
data: T | null
loading: boolean
error: string | null
}
function useFetchData<T>(url: string): FetchDataReturnType<T> {
const [data, setData] = useState<T | null>(null)
const [loading, setLoading] = useState<boolean>(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
const fetchData = async (): Promise<void> => {
try {
const response = await fetch(url)
const jsonData = await response.json()
setData(jsonData)
} catch (e) {
setError('Error fetching data')
} finally {
setLoading(false)
}
}
fetchData()
}, [url])
return { data, loading, error }
}
export default useFetchData
Inside the above example, we are using the useState
hook to manage the state of data
, loading
, and error
. We also use the useEffect
hook to fetch the data from the provided URL when the component mounts and and when the url
state changes.
We also create fetchData
function that makes the API call and sets the data
state if the API call is successful and sets the error
state if an error occurs during the fetch,
We are also explicitly annotating the return type of the hook as FetchDataReturnType
.
Using this custom hook in a component would look like this:
import React from 'react'
import useFetchData from './useFetchData'
type User = {
id: number
name: string
email: string
}
const UserList: React.FC = () => {
const { data, loading, error } = useFetchData<User[]>('https://api.example.com/users')
if (loading) {
return <div>Loading...</div>
}
if (error) {
return <div>{error}</div>
}
return <ul>{data?.map((user) => <li key={user.id}>{user.name}</li>)}</ul>
}
export default UserList
In the above example, we are using our custom hook useFetchData
hook to fetch a list of users from an API. Since our hook is generic, we created a User
type to indicate that the fetched data will be an array of User
objects.
Inside the component, we are accessing the data
, loading
, and error
properties returned from the hook. Based on the state, we can render loading and error messages,
Conclusion
React hooks are a very powerful way for developers to reuse logic across multiple components and make extensible and maintainable code that is easy to read and understand. Using TypeScript with React Hooks provides static typing and helps catch errors early in the development process. After reading this article, I hope you feel confident about building your own custom hooks with TypeScript in your React projects. Happy coding!
If you're interested in learning more about React and TypeScript, check out our React TypeScript guide and the official TypeScript documentation.