0

I am making an Axios call to get data. What should be the type for Axios call? For now, the type is any but how can I make it more strict?

import axios from "axios";
import { createContext, useEffect, useContext, useReducer } from "react";
import { Action, LevelsType, State, StateContextType, StateProviderPropsType } from "../Types/types";

const LevelCtx = createContext({} as StateContextType);

export function LevelProvider({children}:StateProviderPropsType) {

    const initialState:State = {
        levels: null
    }

    const [state, dispatch] = useReducer(reducer, initialState)

    useEffect(() => {
        (async () => {
           const response = await axios.get<any>("http://localhost:5000/levels"); // Fix required.

            return dispatch({type: "SET_LEVELS", payload: response.data.levels})
            

        })();
        
    }, [])


    function reducer(state:State, action: Action) {
        switch (action.type) {
            case "SET_LEVELS":
                return {...state, levels: action.payload }
        
            default:
                return state
        }
    }

    console.log("levelCTX", state);

    return (
        <LevelCtx.Provider value={{ state, dispatch }}>
            {children}
        </LevelCtx.Provider>
    )
};

export function useLevelCtx() {
    return useContext(LevelCtx);
}

My types file looks like this -

import { ReactNode }from "react";

export type LevelsObjectType = {
    _id: string;
    img: string;
    levelName: string;
    __v: number;
}

export type LevelsType = {
    levels: LevelsObjectType[]
}

export type State = {
    levels: LevelsType[] | null;
}

export type StateProviderPropsType = {
    children: ReactNode;
}

export type StateContextType = {
    state: State;
    dispatch: React.Dispatch<Action>;
}

export type Action = 
    | { type: "SET_LEVELS"; payload: LevelsType[] }

export type ServerError = {
    errorMessage: string;
}



The data I am getting from the api call is this - enter image description here

Now after doing this when i try to map over state.levels, it throw the following error -

enter image description here

I tried adding LevelsType to the above axios call but it is throwing an error. I am trying to setup useRecuder and useContext using React TS. I am new to typescript. So please help me figure out this.

3 Answers3

0

You could have two types:

export type LevelsObjectType = {
    _id: string;
    img: string;
    levelName: string;
    __v: number;
}

export type LevelsObjectTypeResult = {
    levelsObjects: LevelsObjectType[],
    success: boolean
}

And return:

await axios.get<LevelsObjectTypeResult>("http://localhost:5000/levels")

In the reducer:

 return {...state, levels: action.payload.levelsObjects }
Samorinho
  • 9
  • 3
  • I tried to do as you said, but that is not working. Also, I have edited the question to be more specific about the problem. Sorry for not being specific earlier. – Harshit Badolla Dec 10 '21 at 18:32
0

You are getting an error because of the way your State type is defined

export type State = {
    levels: LevelsType[] | null;
}

When you do state.levels.map(level), level type is LevelsType which you defined like this

export type LevelsType = {
    levels: LevelsObjectType[]
}

so you are trying to access _id on a LevelsType object and obviously typescript complains about it since _id doesn't exist in LevelsType.

Try this:

export type State = {
    levels: LevelsObjectType[] | null;
}
whygee
  • 1,783
  • 6
  • 12
0

We can destructure Axios and extract the data response from the response object or the Axios response schema. The data property is the server response and virtually will give some sort of type to the response object.

The code I'm putting up will only focus on some types/interfaces in your code and techniques for making Axios get requests without using any data type.

It could be like this for example.

// types

export type LevelsObjectType = {
    _id: string;
    img: string;
    levelName: string;
    __v: number;
}

export type State = {
    levels: LevelsObjectType[]; 
}

export const initialState: State = {
  levels: [], // initial state is empty array and not null
}

// ...

I'm going to separate the business logic of fetching the resources into its own module. e.g. services directory.

import axios from 'axios'
import { LevelsObjectType } from './types'

const clientCall = axios.create({
  baseURL: 'http://127.0.0.1:5000',
  headers: {
    'Content-type': 'application/json',
  },
})

const retrieveAllLevels = async () => {
  // could be translated as ===> const levelsListFromApi: LevelsObjectType[]
  const { data: levelsListFromApi } = await clientCall.get<LevelsObjectType[]>('/levels')
  return levelsListFromApi

// ...

const levelService = {
  retrieveAllLevels,
// ...
}

export default levelService

In the component e.g. App

import React, { useEffect, useContext } from 'react'
import levelService from './services/level'
//...

const App: React.FC = () => {
const { state, dispatch } = useContext(YouStateContext)

useEffect(() => {
    const fetchLevels = async () => {
      try {
        const response = await levelService.retrieveAllLevels()
        //console.log(response)
        dispatch({ type: 'SET_LEVELS', payload: response })
      } catch (error) {
        console.error(error)
      }
    }
    fetchLevels()
  }, [dispatch])

}

const levels = Object.values(state.levels) // begin mapping this array on return
//console.log(levels)

// ...

Just give it a shot, and hopefully it will work.

Happy coding!

aiotrope
  • 51
  • 3