Integrating REST API in ReactJS with fetch & useEffect

Integrating API in a React App can be easily done with simple APIs & file structure. Let’s see how to integrate an API in React app with Fetch & useEffect Hook.

Integrating REST API in ReactJS with fetch & useEffect

Integrating API in a React Project can be easily done by achieving a simple project structure. Let’s see how to integrate an API in a React Project.

We will be building an App to List Current Rates and Currency Convertor using Frankfurter API https://www.frankfurter.app/.

You can use any API, I found this one listed over here: GitHub - public-apis/public-apis: A collective list of free APIs for use

Let’s get started by setting up a project with create-react-app.

npx create-react-app forex-app

cd forex-app

yarn start

This will initialize a new React App named forex-app, start the local development server on port 3000 and open the URL http://localhost:3000 on the default Browser.

We will use the following design for our UI: (many parts of it)
Foreign Currency Rates and Converter Mobile App by Tim on Dribbble

The design proposes the use of Country Flags as currency symbols

As we can see in the above design, there is a list of rates for the preferred currency. We will make this screen work in our design. Let’s add the screen to list the currency exchange rates for a base currency.

The API response structure for the Rates is as follows:

{
  "amount": 1,
  "base": "USD",
  "date": "2020-05-08",
  "rates": {
    "AUD": 1.5321,
    "BGN": 1.8037,
    "BRL": 5.817,
    "...": ...
  }
}

For the above response, the following React Component will display the rates:

import * as React from "react";
import "./RateList.css";

const Amount = ({ amount, rate }) => {
  const _rate = Number(rate);
  return (
    <span className="rate">
      {(amount ? _rate * amount : _rate).toFixed(5)}
    </span>
  );
};

const CurrencyFlag = ({ currency }) => (
  <span className={`
    currency-flag
    currency-flag-${currency.toLowerCase()}
  `}></span>
);

const CSymbol = ({ currency }) => (
  <span className="currency">{currency.toUpperCase()}</span>
);

const display = (currency, reverse) => [
  <CurrencyFlag key={`flag-${currency}`} currency={currency} />,
  <CSymbol key={`symbol-${currency}`} currency={currency} />,
];

const Currency = ({ currency = "usd" }) => (
  <div className="currency-box">{display(currency)}</div>
);

export const RateList = ({ rates = {}, amount, className }) => (
  <div className={`rate-list-container ${className || ''}`}>
    <div className="rate-list">
      <ul>
        {Object.keys(rates).map((currency, index) => (
          <li key={index}>
            <Currency currency={currency} />
            <Amount rate={rates[currency]} amount={amount} />
          </li>
        ))}
      </ul>
    </div>
  </div>
);

And apart from the list, we will need the component to select our preferred currency and set a base amount to convert. The following component will be responsible for that:

import * as React from "react";
import "./CurrencySelector.css";
import { CurrencyFlag } from "../CurrencyFlag";

const currencies = ["EUR", "USD", "GBP"];

const CurrencyFlag = ({ currency }) => (
  <span className={`
    currency-flag
    currency-flag-${currency.toLowerCase()}
  `}></span>
);

const CurrencySelector = ({ currency = "usd", onChangeCurrency }) => (
  <div className="currency-box">
    <select
      className="currency-select"
      value={currency}
      onChange={(e) => onChangeCurrency(e.target.value)}
    >
      {currencies.map((item, index) => (
        <option key={index} >{item}</option>
      ))}
    </select>
    <CurrencyFlag key={`flag-${currency}`} currency={currency} />
  </div>
);

export const SearchBar = ({
  currency = "usd",
  amount = 1,
  onChangeAmount = () => {},
  onChangeCurrency = () => {},
}) => (
  <div className="search-bar-container">
    <div className="search-bar">
      <input
        type="text"
        defaultValue={amount}
        onChange={(e) => onChangeAmount(e.target.value)}
        placeholder="Amount"
      />
      <CurrencySelector
        currency={currency}
        onChangeCurrency={onChangeCurrency}
      />
    </div>
  </div>
);

Let's try to assemble the above components with some mock data:

import React, { useState } from "react";
import { SearchBar } from "../SearchBar/SearchBar";
import { RateList } from "../RateList/RateList";

const rates = {
  "AUD": 1.5321,
  "BGN": 1.8037,
  "BRL": 5.817
}

function App() {
  const [state, setState] = useState({
    rates,
    amount: 1,
    currency: "USD",
  });

  const { amount, currency, rates } = state;
  
  const updateState = (updates = {})  => setState((prev) => ({ ...prev, ...updates }));
  
  const updateAmount = (amount) => updateState({ amount: Number(amount) });

  const updateCurrency = (currency) => updateState({ currency });

  return (
    <div className="app" data-testid="app-container">
      <main className="contents">
        <SearchBar
          amount={amount}
          currency={currency}
          onChangeAmount={updateAmount}
          onChangeCurrency={updateCurrency}
        />
        <RateList className="rates" rates={rates} amount={amount} />
      </main>
    </div>
  );
}

export default App;

Now to fetch the rates from API we will use fetch and the following will be our function to handle all the GET requests:

const baseUrl = "//api.frankfurter.app";

const request = (_url, method = "GET", body = "") => {
  const url = `${baseUrl}${_url}`;
  const headers = new Headers();
  headers.append("Content-Type", "application/json");
  const params = {
    method,
    headers: headers,
  };
  if (["POST", "PUT"].includes(method)) {
    params.body = typeof body !== "string" ? JSON.stringify(body) : body;
  }
  const request = new Request(url, params);

  return fetch(request).then((response) => {
    const { status, headers } = response;
    if (status === 204 || headers.get("Content-Length") === 0) {
      return {};
    }
    return response.json();
  });
};

export const getData = (url) => request(url, "GET");
export const postData = (url, data) => request(url, "POST", data);
export const putData = (url, data) => request(url, "PUT", data);
export const deleteData = (url) => request(url, "DELETE");

With all the necessary pieces in place, we will integrate the API call with getData function in the App Component with the following function and it will be chained to update the state in the Component:

const getRates = (currency) => getData(`/latest?from=${currency}`)

And we will use the React useEffect hook to execute the initial fetch call:

useEffect(() => {
  getRates(state.currency).then(({ rates }) =>
    setState((currentState) => ({
      ...currentState,
      rates,
    }))
  );
}, []);

This will call the API for Rates with fetch on the first call. But we want to fetch rates for any change in the Currency.

As we are already updating the state in the Change Handler of select, we just need to make it; currency from the state; a dependency of useEffect.

In this way, any changes to the dependency will trigger the re-execution of the useEffect hook. The following code will do that:

const App = () => {
  const [state, setState] = useState({
    rates: {},
    amount: 1,
    currency: "USD",
  });
  
  const updateState = (updates = {})  => 
    setState((prev) => ({ ...prev, ...updates }));
  ...
  useEffect(() => {
    getRates(state.currency).then(updateState);
  }, [state.currency]);
  ...
  return (...);
}

Now, if we want to make sure of the newest rates for conversion of amount as well, we will need the following changes to the getRates function and useEffect hook along with the change handlers to update the amount in the state:

const getRates = (currency) => getData(`/latest?from=${currency}`)

export const App = () => {
  const [state, setState] = useState({
    amount: 1,
    currency: "USD",
    rates: {},
  });

  const { amount, currency, rates } = state;

  const updateState = (updates = {})  => setState((prev) => ({ ...prev, ...updates }));

  useEffect(() => {
    getRates(currency).then(updateState);
  }, [currency]);

  const updateAmount = (amount) => updateState({ amount: Number(amount) });

  const updateCurrency = (currency) => updateState({ currency });

  return (...);
}

You can play with the Code and demo at the following links:

Github Repo Demo

Conclusion

Here we saw the following things:

  • Starting React app with create-react-app
  • Using Hooks to maintain state with useState
  • Using fetch in React Project
  • useEffect for reacting to state changes and making API requests
How do you call APIs in your React project?

Let me know through comments ? or on Twitter at  @patelpankaj  and  @time2hack

If you find this article helpful, please share it with others ?

Subscribe to the blog to receive new posts right to your inbox.


Credits