TypeOfNaN

Writing Custom React Hooks with Typescript

Nick Scialli
September 19, 2020

React logo

Writing custom React hooks with Typescript isn’t all that different from writing them in plain old JavaScript. In this post, we review some basics and “gotchas” of writing your own React hooks with Typescript.

Example: A Simple useFetch Hook

To demonstrate how to write our hooks, we’ll write a hook that does something pretty ubiquitous in web applications: fetching data. Our hook will be called useFetch.

Importantly, we’re just going through the basics here, so this will be a very bare-bones hook. If you want to actually use a useFetch hook in production, I recommend googling around for an existing one that has a lot more bells and whistles.

Making GET Requests

To make a get request with our hook, we know we need a URL, and we need to return some fetched data. Our useFetch hook, therefore, will have a url parameter that’s a string, and will use the useState hook built-in to react store our fetched data.

import { useState } from 'react';

function useFetch(url: string) {
  const [data, setData] = useState(null);

  // Fetch the data here

  return data;
}

Now the fun part: let’s fetch and set our data! We can simply use our window’s fetch method and setData to the response.

import { useState } from 'react';

function useFetch(url: string) {
  const [data, setData] = useState(null);

  // Fetch the data here
  fetch(url)
    .then((res) => {
      return res.json();
    })
    .then((res) => {
      setData(res);
    });

  return data;
}

Let’s see this in action by using our hook in an app.

import { useFetch } from './useFetch';

function App() {
  const data = useFetch('https://api.github.com/users/nas5w');

  return data ? <>{JSON.stringify(data)}</> : <>Loading...</>;
}

export default App;

And this works: We’ll see a Loading... message while our fetch is happening and then our stringified github data will display.

But this isn’t good enough: our application thinks the type of data is null since that’s the only type we explicitly gave our stateful data variable in our hook.

Not Good Enough! We Need to Type the Return Value

To allow ourselves to type the return value from our useFetch hook, we can use Typescript generics to pass a type to our hook. Let’s see how this would look.

import { useState } from 'react';

function useFetch<D>(url: string) {
  const [data, setData] = useState<D | null>(null);

  // Fetch the data here
  fetch(url)
    .then((res) => {
      return res.json();
    })
    .then((res) => {
      setData(res);
    });

  return data;
}

So now we’ve told our hook that the hook must be provided a type, D, and the type of data will be D or null. We can now revisit our App.tsx file and adjust how we use this hook.

For this exercise, I have created a basic GithubResponse type. It’s probably not 100% correct, but good enough for this demonstration!

import { useFetch } from './useFetch';

function App() {
  const data = useFetch<GithubResponse>('https://api.github.com/users/nas5w');

  return data ? (
    <>
      Name: {data.login}
      <br />
      Followers: {data.followers}
    </>
  ) : (
    <>Loading...</>
  );
}

export default App;

type GithubResponse = {
  login: string;
  id: number;
  node_id: string;
  avatar_url: string;
  gravatar_id: string;
  url: string;
  html_url: string;
  followers_url: string;
  following_url: string;
  gists_url: string;
  starred_url: string;
  subscriptions_url: string;
  organizations_url: string;
  repos_url: string;
  events_url: string;
  received_events_url: string;
  type: string;
  site_admin: boolean;
  name: string;
  company: string | null;
  blog: string | null;
  location: string | null;
  email: string | null;
  hireable: string | null;
  bio: string;
  twitter_username: string | null;
  public_repos: number;
  public_gists: number;
  followers: number;
  following: number;
  created_at: string;
  updated_at: string;
};

As we can see, our code compiles, meaning Typescript understands our returned object type!

github response data

TL;DR: Generics Make Your Hooks Work

The TL;DR here is that Typescript generics are key to making your React Hooks work in Typescript!

If you'd like to support this blog by buying me a coffee I'd really appreciate it!

Nick Scialli

Nick Scialli is a senior UI engineer at Microsoft.

© 2024 Nick Scialli