Juan
Juan

Reputation: 809

How can I type a custom hook to make it reusable

I am doing a custom hook that tries to fetch requests. I need to type response to make it reusable.

import { useState, useEffect } from 'react'

export function useFetch (url: string, options?: object) {
    const [response, setResponse] = useState({});
    const [error, setError] = useState({});

    useEffect(() => {
      const fetchData = async () => {
        try {
          const res = await fetch(url, options);
          const json = await res.json();
          setResponse(json);
        } catch (error) {
          setError(error);
        }
      };
      fetchData();
    }, []);

    return { response, error };
  };

When I use it from a component, I create the specific interface for that answer, but that's where the error is. Because the interface I put to the response when structuring useFetch is different from the type of the custom hook.

Type '{ response: {}; error: {}; }' is not assignable to type '{ response: IResponse; error: object; }'.
  Types of property 'response' are incompatible.

This is the component that I import useFetch

import React, { FunctionComponent } from 'react'
import { useFetch } from '../../hooks/useFetch'

export const ListOfMovies: FunctionComponent = () => {
    const { response, error }: { response: IResponse, error: object} = useFetch(
        "http://www.omdbapi.com/?apikey=****&s=batman&page=2"
    )
    
    if (!response) return <h2>Loading...</h2>

    if (error) return <h2>Error</h2>

    return (
        <div>
            <h2>Lista de películas</h2>
            {response.Search.map(movie => (
                <p>{movie.Title}</p>
            ))}
        </div>
    )
}

interface IResponse {
    Search: Array<IMovie>
}

interface IMovie {
    Title: string,
    Year: string,
    imdbID: string,
    Type: string,
    Poster: string,
}

Upvotes: 0

Views: 424

Answers (2)

Tadhg McDonald-Jensen
Tadhg McDonald-Jensen

Reputation: 21453

make your component generic?

export function useFetch<ResponseType>(url: string, options?: object) {
    const [response, setResponse] = useState<ResponseType | {}>({});
    ...

then call it like:

const { response, error } = useFetch<IResponse>(
    "http://www.omdbapi.com/?apikey=****&s=batman&page=2"
)

Note that without a valid default value for the state the state had to be set to T | {} which makes it less useful but the solution to that is application specific depending on what you want to do.

Upvotes: 1

glinda93
glinda93

Reputation: 8459

Set type for your states:

const [response, setResponse] = useState<IResponse|{}>({});
const [error, setError] = useState<object|{}>({});

Upvotes: 0

Related Questions