Typescript React – Fetch array data using Axios

This is a quick post to help detail how you can fetch data from an API without Typescript shouting at you! It details a way to fetch data using Axios and set them to variables with defined Types as required by Typescript.

For this example our app will be made with Typescript and React. We’ll use Axios to retrieve our data rather than the standard fetch api that comes with Javascript. The data that we want to display will be held within an array where we’ll want to access various elements within it. While Axios will ultimately be used to fetch external data, for our purposes we’ve just set it up to read a file from within our repo.

If you’d like to follow along from scratch, then instructions are provided in the collapsible section below, otherwise download the final repo here.

Installation from scratch
  • Following the instructions from create-react-app, make a new typescript based application:
    npx create-react-app axios-example --template typescript
    or
    yarn create react-app axios-example --template typescript

    NB: npx uses ‘create-react-app’ with 2 hyphens, yarn uses ‘create react-app’ with only 1 hyphen between react and app. That was annoying…

  • Move into your new app:
    cd axios-example

The Problem

Initial attempts at retrieving an array from a data source resulted in the following error:

Element implicitly has an 'any' type because expression of type
'number' can't be used to index type 'Promise<DailyPrice[]>'

Ultimately I got myself in a bit of a tangle with how to handle Promises in Typescript and then accessing an element within an array that should be returned.

The Solution

We’ll render the data in the App.tsx file for convenience. To handle the Axios call we’ll make a couple of files:

  1. fetchers.tsx file within a ‘fetchers’ folder which will house our Axios call;
  2. use-queries.tsx custom hook within a ‘hooks’ folder that will be called from App.tsx. This will utilise the fetchers function noted above.

We’ll also need some data to work with. For this example I’ve used the ‘endofdayquotes/history’ endpoint from the MorningStar API held on RapidAPI.com. The below data is a snippet from their test page. If you want to follow along, make a folder in the ‘/public’ directory and create a file named “DUMMY_DATA.json”. Copy and paste the data into that file:

Data
{
"total": 2518,
"offset": 0,
"results": [
{
"date": "2010-02-01",
"open": "28.4",
"high": "28.48",
"low": "27.92",
"last": "28.41",
"volume": 85931096
},
{
"date": "2010-02-02",
"open": "28.37",
"high": "28.5",
"low": "28.14",
"last": "28.46",
"volume": 54413656
},
{
"date": "2010-02-03",
"open": "28.22",
"high": "28.79",
"low": "28.12",
"last": "28.63",
"volume": 61397848
},
{
"date": "2010-02-04",
"open": "28.36",
"high": "28.5",
"low": "27.81",
"last": "27.84",
"volume": 77849968
}
],
"responseStatus": null
}
view raw DUMMY_DATA.json hosted with ❤ by GitHub
Dummy data

Given the above data set we’ll also need some Types. ‘DailyPrices’ is of the same format that comes out of the API. We’ve made another Type called DailyPrice which is an array of daily price details (a subset from DailyPrices). We’ve also included an ‘APIResponse’ type which is a destructured form of our DailyPrices Type. Make a new file named types.tsx under a new folder named ‘types’:

Types
export interface DailyPrices {
total: number,
offset: number,
results: DailyPrice[],
responseStatus: string | null
}
export interface DailyPrice {
date: string,
open: number,
high: number,
low: number,
last: number,
volume: number
}
export interface APIResponse {
results: DailyPrice[]
}
view raw types.tsx hosted with ❤ by GitHub
Type definitions

Fetchers.tsx

In our fetchers file we’ll use the following code:

import axios from 'axios';
export async function get<T>(
path: string
): Promise<T> {
const { data } = await axios.get(path);
return data;
};
view raw fetchers.tsx hosted with ❤ by GitHub
Generic Axios Fetcher

This is a helper function that makes fetching data a little easier and reusable. The ‘get’ function takes a path variable that is the location of our data. We can also pass in the Type of the data that we expect to get back. This is shown with the generic <T> that is passed to get and also shows up in the Promise that gets returned from the function. Writing the code in this way means we can reuse the get function for other data types.

Data provided back from axios will come in the format of {data: ...}. Therefore, we destructure our response with const { data } = ....

As the process is asynchronous, we need to label the function with async and put await before our axios call so that we let the application know to wait for the results to come back. Finally we return the data.

use-queries.tsx

Our use-queries file will call the axios get function noted above and has the following code:

import { useState, useEffect } from 'react';
import { DailyPrice, APIResponse } from '../types/types';
import { get } from '../fetchers/fetchers';
export const useGetDailyPrices = () => {
const [data, setData] = useState<DailyPrice[]>([]);
const getData = async () => {
const { results } = await get<APIResponse>('./data/DUMMY_DATA.json');
setData(results)
}
useEffect(() => {
getData()
}, [])
return data;
}
view raw use-queries.tsx hosted with ❤ by GitHub
Query hook to fetch data

First, we define an element in state that will hold our data. We initialise this as an empty array of ‘DailyPrice[]’ type. This is what we want to pass back to the app – an array of DailyPrice data.

We create an asynchronous function called getData which awaits our get helper function we made above. We provide it with the path to our data and also give a type that we expect to receive. In this instance, it’s an ‘APIResponse’ type which provides an array of daily prices linked to a key of ‘results’. Then we set the internal state of that component to the value of results.

Without async/await for this function, you may see the following error:

Property 'results' does not exist on type 'Promise<APIResponse>'.

This is because we’re passing back a Promise rather than the data type that we want to work with and sets the type of results to any. Async/Await here ensures that we wait for the Promise to resolve and set the value of the variable to our desired data type.

The getData function is called within useEffect. This replaces the previously used componentDidMount lifecycle method (among others) which was superseded in React v16.8. useEffect gets called in various stages of the component lifecycle and so, as noted in Chidume Nnamdi’s post, we need to pass in a dependency array to stop the method being called continuously. This is where the empty array on line 15 comes into play.

So, to summarise this component fetches the data, sets the return value to its internal state and then passes that to the where the function was called from. Now to change App.tsx.

App.tsx

We’ve deleted everything between the <header> elements that come as default within create-react-app. All we’re going to do is have two sections that detail the closing price from the first and last entries in the data.

import React from 'react';
import './App.css';
import { useGetDailyPrices } from './hooks/use-queries';
function App() {
const data = useGetDailyPrices()
if (data.length === 0) return null;
return (
<div className="App">
<header className="App-header">
<h2>Stock Prices</h2>
<div className="row">
<p>First</p>
<p>{data[0].last}</p>
</div>
<div className="row">
<p>Last</p>
<p>{data[data.length – 1].last}</p>
</div>
</header>
</div>
);
}
export default App;
view raw App.tsx hosted with ❤ by GitHub
App component which renders our data

In the code above, we’ve created a variable named data that calls our useGetDailyPrices hook defined above. As we initially define the value of our data as an empty array in the hook, we need to include a conditional so that we don’t render anything until we get data back. Otherwise, the app will fall over when we call data[0] and we’ll receive the following error:

TypeError: Cannot read property 'last' of undefined

There’s nothing much else to this component. We place the conditional to return nothing if we don’t have any data set, otherwise we can now access the array elements within the data variable. As we’ve set the async/await functionality to our hook, the data in App.tsx is now of a DailyPrice[] type rather than a Promise which means we avoid the error noted at the start of this post.

For completeness, here’s the final outcome (with some minor CSS involved!):

Rendered output

I hope this helps in getting data from Axios within a Typescript based React app. If you have any questions or suggestions then please let me know and I can address/incorporate them.

Further reading

Robin Weiruch – How to fetch data with React Hooks?

Chidume Nnamdi – Fetching data in React using Hooks


If you enjoyed reading this post, please consider reading some of my other React posts:

React – How to convert to Hooks – State

React – How to convert to Hooks – Props


If you enjoyed this type of content and would like to see more, feel free to check out my social media accounts:

Twitter: @sdrobertsCode
LinkedIn
GitHub