Abdalla Suliman
Abdalla Suliman

Reputation: 21

TypeScript fetch api with react

I created a custom hook to fetch an API and I wanna pass the data to the component and getting this type of error. I'm new to react and typescript

my custom useFetch code:

import { useEffect, useState } from "react";

export interface IBlog {
    data: {
        title: string;
        body: string;
        author: string;
        id: number
        url: string;
    }[]
}
const useFetch = (url: string) => { // url: string

    const [data, setData] = useState<IBlog['data']>([]);
    const [isPending, setIsPending] = useState(false);
    const [error, setError] = useState(null);

    useEffect(() => {
        const abortController = new AbortController();


        setTimeout(() => {
            fetch(url, { signal: abortController.signal })
                .then(res => {
                    if (!res.ok) { // error coming back from server
                        throw Error('could not fetch the data for that resource');
                    }
                    return res.json();
                })
                .then(data => {
                    setIsPending(false);
                    setData(data);
                    setError(null);
                })
                .catch(err => {
                    if (err.name === "AbortError") {
                        console.log('fech aborted');
                    } else {
                        // auto catches network / connection error
                        setIsPending(false);
                        setError(err.message);
                    }
                })
        }, 1000);

        // cleanup funtion
        return () => abortController.abort();

    }, [url])

    return [data, isPending, error]
}

export default useFetch

and this is the component where I want to pass the data and getting my error

import BlogList from "./BlogList";
import useFetch from "./useFetch";
import React, { FC } from 'react';
import { IBlog } from "./useFetch"

const Home: FC = () => {

    const [data, isPending, error] = useFetch("http://localhost:8000/blogs")

    console.log(data, isPending, error, "from hook")

    return (
        <div className='home'>
            {error && <div>{error}</div>}
            {isPending && <div>loading...</div>}
            {data && <BlogList data={data} />}
        </div>
    )
}

export default Home

and this is the component to display my data

import BlogList from "./BlogList";
import useFetch from "./useFetch";
import React, { FC } from 'react';
import { IBlog } from "./useFetch"

const Home: FC = () => {

    const [data, isPending, error] = useFetch("http://localhost:8000/blogs")

    console.log(data, isPending, error, "from hook")

    return (
        <div className='home'>
            {error && <div>{error}</div>}
            {isPending && <div>loading...</div>}
            {data && <BlogList data={data} />}
        </div>
    )
}

export default Home

Error messege

    Failed to compile
    C:/Users/63906/Documents/Web Development/citcs-blogs/src/components/Home.tsx
    TypeScript error in C:/Users/63906/Documents/Web Development/citcs-blogs/src/components/Home.tsx(16,26):
    Type 'true | { title: string; body: string; author: string; id: number; url: string; }[]' is not assignable to type '{ title: string; body: string; author: string; id: number; url: string; }[]'.
      Type 'boolean' is not assignable to type '{ title: string; body: string; author: string; id: number; url: string; }[]'.  TS2322
    
        14 |       {error && <div>{error}</div>}
        15 |       {isPending && <div>loading...</div>}
      > 16 |       {data && <BlogList data={data} title='All Blogs!' />}
           |                          ^
        17 |     </div>
        18 |     )
        19 | }

Upvotes: 2

Views: 3047

Answers (1)

aleksxor
aleksxor

Reputation: 8340

The problem is you're returning a mutable array from useFetch custom hook. And it's type gets inferred as a union of all types of the values it contains:

/*
const useFetch: (url: string) => (boolean | {
    title: string;
    body: string;
    author: string;
    id: number;
    url: string;
}[] | null)[]
*/
const useFetch = (url: string) => {
...
  return [data, isPending, error];
};

To fix the issue you just have to annotate it as a readonly type:

/*
const useFetch: (url: string) => readonly [{
    title: string;
    body: string;
    author: string;
    id: number;
    url: string;
}[], boolean, null]
*/
const useFetch = (url: string) => {
...
  return [data, isPending, error] as const;
};

Upvotes: 1

Related Questions