Home > database >  React generic useFetch state
React generic useFetch state

Time:08-07

I'm trying to write a custom hook useFetch that makes many requests for different data.

The problem is that I can't typing my date correctly, I have state like this.

interface IState<T> {
   data:  T | T[]
   loading: boolean,
   error: boolean,
}

Initial state

const initialState = {
   data: [],
   loading: false,
   error: false,
}

useEffect

useEffect(() => {
        setData((prevState) => ({...prevState, loading: true}));
        axios.get(url)
            .then(response => response.data.results ? response.data.results as T[] : response.data as T)
            .then((data) => (setData((prevState) => ({...prevState, loading: false, data}))))
            .catch(e => {
                setData((prevState) => ({...prevState, error: true}));
                if (e instanceof Error) console.log(e.message);
            })
}, []);

When i get response.data.results i have array of objects and when i have response.data i have only one object. I'm trying to return the right type.

I call my function like this useFetch and I have result like this MovieType | MovieType[]. I don't need TypeGuard I need only one correct type.

If you see any problems please let me know.

Thanks a lot!

CodePudding user response:

Axios.get<T> is a generic function, so you can take advantage of this.

Try

useEffect(() => {
        setData((prevState) => ({...prevState, loading: true}));
        axios.get<{results: T[]} | T>(url)
            .then(({data}) => "results" in data ? data.results : data)
            .then(data => {
               setData(prevState => ({...prevState, loading: false, data}))
            })
            .catch(e => {
                setData((prevState) => ({...prevState, error: true}));
                if (e instanceof Error) console.log(e.message);
            })
}, []);

See playground example here

CodePudding user response:

It's impossible for this example to have a singular type when unionized.

The generic function has no idea which one it will output as T[] or a T object because of how you defined IState.

You would just have to check if it's an array or not when you read .data from the hook.

The only way to have a singular output type would be to remove the union T[] and simply cast both like Sana Mumtaz said.

interface IState<T> {
   data:  T
   loading: boolean,
   error: boolean,
}

const useFetch = <T>(): IState<T> => {
   // ...
   response => (response.data.results ? response.data.results : response.data) as T
}

useFetch<MovieType[]>();
  • Related