I have a useFetch, to whom I want to send an argument URL of type string, forgot to mention it is to consume an API. The problem I have is that it does not recognize the result. I am using generic types in useFetch. If I use the types created in my interface everything is clean and there are no errors.
But my idea is that useFetch should be agnostic. The error I get from the linter is Property results does not exist on type <useFetchState<movieApi | null>>. In the useFetch file, I have another error. The argument of type null is not assignable to parameter <useFetchState<movieApi | null>>.
useFetch code.
import { useEffect, useState } from "react"
type useFetchState<T> = {
data: null | T;
}
export const useFetch = <T>( url: string ) => {
const [data, setData] = useState<useFetchState<T | null>>(null)
const [loading, setLoading] = useState<boolean>(false)
const [error, setError] = useState<string | null>(null)
const getData = (url:string) => {
setLoading(true)
return fetch(url)
.then((res) => {
const showError = {
err:true,
statuscode:res.status,
message:"Error in your request"
}
if(!res.ok){
throw showError;
}
return res.json()
})
.then((data) => {
setData(data)
})
.catch((err) => setError(err))
.finally(() => {
setLoading(false);
})
}
useEffect(() => {
getData(url)
}, [url])
return {data, loading, error}
}
Home component code.
import { movieApi, movieApiResult } from "../../hooks/useFetch/interface";
import { useFetch } from "../../hooks/useFetch/useFetch";
import "./home.css";
const Home = () => {
const IMGPATH = "https://image.tmdb.org/t/p/w1280";
const { data } = useFetch<movieApi>(
"https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=04c35731a5ee918f014970082a0088b1&page=2"
);
return (
<div className="container">
{data?.results.map((item: movieApiResult) => (
<div
key={item.id}
className="item"
//onClick={() => handleClick(item.id)}
>
<img
src={`${IMGPATH}${item.poster_path}`}
alt={item.title}
className="image"
/>
<p>{item.title}</p>
<span>{item.vote_average}</span>
</div>
))}
</div>
);
};
export default Home;
interfaces
export interface movieApi {
page: number;
results: movieApiResult[];
total_pages: number;
total_results: number;
}
export interface movieApiResult {
adult: boolean;
backdrop_path: string;
genre_ids: number[];
id: number;
original_language: string;
original_title: string;
overview: string;
popularity: number;
poster_path: string;
release_date: string;
title: string;
video: boolean;
vote_average: number;
vote_count: number;
}
ok,removing the useFetchState works, the problem is fixed, but if I create a useContext, and send that data to the Home component, the error appears again in the "results".
type props = {
children: JSX.Element | JSX.Element[];
};
interface MoviesTypes {
data: movieApi | movieObject | null;
loading: boolean;
setUrl: Dispatch<SetStateAction<string>>;
handleClick: (id: number) => void;
IMGPATH: string;
}
const MoviesContext = createContext<MoviesTypes>({} as MoviesTypes);
export const MoviesProvider = ({ children }: props) => {
const [url, setUrl] = useState<string>(
"https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=04c35731a5ee918f014970082a0088b1&page=2"
);
const [ids, setIds] = useState<null | number>(null);
const { data, loading } = useFetch<movieApi | movieObject>(url);
const IMGPATH = "https://image.tmdb.org/t/p/w1280";
const handleClick = (id: number) => {
setUrl(
`https://api.themoviedb.org/3/movie/${id}?api_key=04c35731a5ee918f014970082a0088b1`
);
setIds(id);
};
return (
<MoviesContext.Provider
value={{
data,
loading,
setUrl,
handleClick,
IMGPATH,
}}
>
{children}
</MoviesContext.Provider>
);
};
export const useMovies = () => {
const context = useContext(MoviesContext);
return context;
};
I guess it must be a fairly easy problem. But I'm just getting started with typescript. Thank you very much, best regards.
CodePudding user response:
Your use of useFetchState
is slightly off - its type is
{
data: null | T;
}
but then you pass in a value that must match it (through useState
) into it not an object with a possibly null data
, but null
itself.
useState<useFetchState<T | null>>(null)
The useFetchState
isn't doing much for you anyway. I'd change the useState
type to be just null or T, no need for anything else.
const [data, setData] = useState<T | null>(null)
CodePudding user response:
In this case, you do not need this:
type useFetchState<T> = {
data: null | T;
}
Just change your useFetch
like below (don't forget the <T,>
if you are in .tsx) :
export const useFetch = <T,>(url: string) => {
const [data, setData] = useState<T>();
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
...
}
and everything will be OK.