Integrating REST API in ReactJS with fetch & useEffect

Integrating API in a React Project can be easily done by achieving 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](https://github.com/facebook/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 following design for our UI: (many parts of it)
Foreign Currency Rates and Convertor Mobile App by Tim on Dribbble

The design proposes the use of Country Flags for currency symbols

As we can see in above design, there is a list of rates for the preferred currency. We will make this screen working 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 above response, 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. 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>
);

Lets 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 updateAmount = (amount) =>
    setState((currentState) => ({
      ...currentState,
      amount: Number(amount),
    }));

  const updateCurrency = (currency) =>
    setState((currentState) => ({
      ...currentState,
      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 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");

export default {
  get: getData,
  post: postData,
  put: putData,
  delete: deleteData,
};

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}`
).then(({ rates }) =>
  setState((currentState) => ({
    ...currentState,
    rates,
  }))
);

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

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

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 state; a dependency of useEffect.

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

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

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

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

  const getRates = (currency, amount = 1) => getData(
    `/latest?from=${currency}&amount=${amount}`
  ).then(({ rates }) =>
    setState((currentState) => ({
      ...currentState,
      rates,
    }))
  );
  useEffect(() => {
    getRates(state.currency, state.amount);
  }, [state.currency, state.amount]);

  ...
  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 make 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


Originally published at https://time2hack.com on May 11, 2020.

No Comments Yet