Fetch Data with Type Safety in React#

Last Updated: October 3, 2023

My project source code is available here

Overview#

In this tutorial, we will learn how to fetch data with type safety in React using TypeScript and the Fetch API.

Files#

BitcoinData.tsx#

Note

The TypeScript type bitcoinDataType is used to define the shape of the data response from the Fetch API. This is used to ensure type safety when accessing the data.

Also, when we initialize the state of the coinData object, we use the bitcoinDataType type to ensure type safety. The initial state of the coinData object is an empty bitcoinDataType object.

const [coinData, setCoinData] = useState<bitcoinDataType>({
    bpi: {
      EUR: {
        code: "",
        description: "",
        rate: "",
        rate_float: 0,
        symbol: "",
      },
      GBP: { code: "", description: "", rate: "", rate_float: 0, symbol: "" },
      USD: { code: "", description: "", rate: "", rate_float: 0, symbol: "" },
    },
    chartName: "",
    disclaimer: "",
    time: { updated: "", updatedISO: "", updateduk: "" },
  });
BitcoinData.tsx
import { useEffect, useState } from "react";

type bitcoinDataType = {
  time: {
    updated: string;
    updatedISO: string;
    updateduk: string;
  };
  disclaimer: string;
  chartName: string;
  bpi: {
    USD: {
      code: string;
      symbol: string;
      rate: string;
      description: string;
      rate_float: Number;
    };
    GBP: {
      code: string;
      symbol: string;
      rate: string;
      description: string;
      rate_float: Number;
    };
    EUR: {
      code: string;
      symbol: string;
      rate: string;
      description: string;
      rate_float: Number;
    };
  };
};

function BitcoinData() {
  // State
  const [coinData, setCoinData] = useState<bitcoinDataType>({
    bpi: {
      EUR: {
        code: "",
        description: "",
        rate: "",
        rate_float: 0,
        symbol: "",
      },
      GBP: { code: "", description: "", rate: "", rate_float: 0, symbol: "" },
      USD: { code: "", description: "", rate: "", rate_float: 0, symbol: "" },
    },
    chartName: "",
    disclaimer: "",
    time: { updated: "", updatedISO: "", updateduk: "" },
  });
  async function fetchData() {
    fetch("https://api.coindesk.com/v1/bpi/currentprice.json")
      .then((response) => response.json())
      .then((data) => setCoinData(data))
      .catch((error) => console.log(error));
  }

  // Only fetch data on first render
  useEffect(() => {
    fetchData();
  }, []);

  useEffect(() => {
    console.log(coinData);
    console.log("length: ", Object.keys(coinData).length);
  }, [coinData]);

  return Object.keys(coinData).length === 0 ? (
    <div>
      <h1>Loading data</h1>
    </div>
  ) : (
    <div>
      <h1>{"USD: $" + coinData["bpi"]["USD"]["rate_float"]}</h1>
      <h2>{"Last Updated: " + coinData["time"]["updated"]}</h2>
    </div>
  );
}

export { BitcoinData };

App.tsx#

This file simply renders the <BitcoinData /> component.

App.tsx
import React from "react";
import "./App.css";
import { BitcoinData } from "./BitcoinData";

function App() {
  return (
    <div className="App">
      <BitcoinData />
    </div>
  );
}

export default App;

index.tsx#

This file is the entry point for the React application.

index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

References#