Reputation: 73
import * as React from "react";
import axios from "axios";
import {Fragment, useState, useEffect} from "react";
interface Ihits {
objectID: string;
url: string;
title: string;
}
interface IinitialData<T> {
hits: Array<T>
}
const useDataApi = (initialUrl:string, initialData: IinitialData<Ihits>) => {
const [data, setData] = useState(initialData);
const [url, setUrl] = useState(initialUrl);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const result = await axios(url);
setData(result.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
fetchData();
}, [url]);
// return [{ data, isLoading, isError }, setUrl];
return [{data, isLoading, isError }, setUrl];
};
const UseDataFetch: React.FC = () => {
const [query, setQuery] = useState('redux');
**const [{data, isLoading, isError}, setUrl] = useDataApi(**
'https://hn.algolia.com/api/v1/search?query=redux',
{ hits: [] },
);
return (
<Fragment>
<form
onSubmit={event => {
**setUrl(**
`http://hn.algolia.com/api/v1/search?query=${query}`,
);
event.preventDefault();
}}
>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button type="submit">Search</button>
</form>
{isError && <div>Something went wrong ...</div>}
{isLoading ? (
<div>Loading ...</div>
) : (
<ul>
{data.hits.map((item: Ihits) => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
)}
</Fragment>
);
}
export default UseDataFetch;
I studied this example which linked https://www.robinwieruch.de/react-hooks-fetch-data
i tried various ways of solution but couldn't set typed destructuring value at all. except using any type and object return like (eg: return {data, isLoading, isError , setUrl};)
it will be thankful someone could tell what i missed
Upvotes: 3
Views: 1959
Reputation: 5249
Or just add as const
to the return parameter in your custom hook.
return [whatever, whatEver] as const
Upvotes: 2
Reputation: 116
Typescript fails to infer it because you are returning array where each element is of different type. From Typescript's point of view, there is no way it can relate them without you explicitly typing it.
return [{data, isLoading, isError}, setUrl];
First approach would be that you explicitly define return type of useDataApi
method:
const useDataApi = (initialUrl:string, initialData: IinitialData<Ihits>):
[{data: IinitialData; isLoading: boolean; isError: boolean}, string] => {
// your code here
}
Other approach would be that you return object of specific type instead of array. You can define custom type:
type DataApiResponse = {
response: {
data: IinitialData<Ihits>;
isLoading: boolean;
isError: boolean;
};
setUrl: Dispatch<SetStateAction<string>>;
}
And then utilize it as:
return {response: {data, isLoading, isError}, setUrl} as DataApiResponse;
Finally make changes in UseDataFetch
method:
const {response, setUrl} = useDataApi(
"https://hn.algolia.com/api/v1/search?query=redux",
{ hits: [] }
);
const {data, isError, isLoading} = response;
Upvotes: 4
Reputation: 11581
If TypeScript fails to infer it, then you can just explicitly declare the return type of the function yourself:
// Note return type added after ':'
const useDataApi = (initialUrl:string, initialData: IinitialData<Ihits>): [{data: IinitialData; isLoading: boolean; isError: boolean}, string] => {
const [data, setData] = useState(initialData);
const [url, setUrl] = useState(initialUrl);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const result = await axios(url);
setData(result.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
fetchData();
}, [url]);
// return [{ data, isLoading, isError }, setUrl];
return [{data, isLoading, isError }, setUrl];
};
Upvotes: 0