DEV Community

Cover image for Currency Converter + Rates
Deepak Singh Kushwah
Deepak Singh Kushwah

Posted on

Currency Converter + Rates

Hello Developers,

This app is my new react project to learn about state + axios + routes in react js. It shows the exchage rates and convert currency value from one to other. I have used following packages to create this app...

  1. Axios
  2. React Router Dom
  3. React Bootstrap + Bootstrap

Use npm install and npm start to run this project

So to create this app, I have used following code.

App.js

import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";
import Navigation from "./components/shared/Navigation";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import CurrencyConverter from "./pages/CurrencyConverter";
import CurrencyRates from "./pages/CurrencyRates";
function App() {
  return (
    <div className="App">
      <Router>
        <Navigation />
        <div className="container">
          <Routes>
            <Route path="/" element={<CurrencyRates />} />
            <Route path="/currency-converter" element={<CurrencyConverter />} />
          </Routes>
        </div>
      </Router>
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

To style loader in this app, update App.css to following...

.loadingSpinnerContainer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 5000;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .loadingSpinner {
    width: 64px;
    height: 64px;
    border: 8px solid;
    border-color: #00cc66 transparent #00cc66 transparent;
    border-radius: 50%;
    animation: spin 1.2s linear infinite;
  }
Enter fullscreen mode Exit fullscreen mode

Now we need to created pages to load currency exchange rates and converter. So i created pages folder in src. Add following files in pages folder.

pages/CurrencyConverter.jsx

import React, { useEffect, useState } from 'react'
import http from '../components/CustomAxios'
import Spinner from '../components/shared/Spinner';
function CurrencyConverter() {
  const [symbols, setSymbols] = useState([]);
  const [loading, setLoading] = useState(false);

  const [fromCurrency, setFromCurrency] = useState('');
  const [toCurrency, setToCurrency] = useState('');
  const [amount, setAmount] = useState(0);
  const [convertedAmount, setConvertedAmount] = useState(null);
  useEffect(() => {
    setLoading(true);
    const getSymbols = async () => {
      const sys = await http.get("/symbols");
      const data = await sys.data;
      let arr = Array.from(Object.keys(data.symbols), k => [`${k}`, data.symbols[k]]);
      setSymbols(arr);
      setLoading(false);
    };

    getSymbols();
  }, []);



  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);

    const res = await http.get('/convert?from=' + fromCurrency + '&to=' + toCurrency + "&amount=" + amount + "&places=2");
    const data = await res.data;
    //console.log(data);
    setConvertedAmount(data.result);
    setLoading(false);
  }

  const handleSelectChange = (e) => {
    setConvertedAmount(null);
    if(e.target.id === 'toCurrency'){
      setToCurrency(e.target.value);
    }

    if(e.target.id === 'fromCurrency'){
      setFromCurrency(e.target.value);
    }
  }

  if (loading) {
    return <Spinner />
  }
  return (
    <>
      <h1>Convert Currency</h1>
      <form onSubmit={handleSubmit}>
        <table className='table table-borderless table-hover'>
          <tbody>
            <tr>
              <td width="30%">From Currency</td>
              <td width="70%">
                <select className='form-control' id="fromCurrency" onChange={handleSelectChange}>
                  {symbols.length > 0 && symbols.map((item) => (
                    <option value={item[0]} key={item[1].code}>{item[1].code} - {item[1].description}</option>
                  ))}
                </select>
              </td>
            </tr>
            <tr>
              <td>To Currency</td>
              <td>
                <select className='form-control' id="toCurrency" onChange={handleSelectChange}>
                  {symbols.length > 0 && symbols.map((item) => (
                    <option value={item[0]} key={item[1].code}>{item[1].code} - {item[1].description}</option>
                  ))}
                </select>
              </td>
            </tr>
            <tr>
              <td>Amount</td>
              <td><input className='form-control' type="number" onChange={(e) => setAmount(e.target.value)} /></td>
            </tr>
          </tbody>
          <tfoot>
            <tr>
              <td>&nbsp;</td>
              <td><button className='btn btn-primary' type="submit">Convert</button></td>
            </tr>
            {convertedAmount !== null &&
              <tr>
                <td>Converted Amount</td>
                <td>{toCurrency} {convertedAmount}</td>
              </tr>
            }
          </tfoot>

        </table>
      </form>
    </>
  )
}

export default CurrencyConverter
Enter fullscreen mode Exit fullscreen mode

pages/CurrencyRates.jsx

import React, { useEffect, useState } from 'react'
import ExchangeRate from '../components/ExchangeRate'
import http from '../components/CustomAxios'
function CurrencyRates() {
  const [rates, setRates] = useState([]);
  const [baseCurrency, setBaseCurrency] = useState('Eur');
  const [loading, setLoading] = useState(true);
  const [exchangeDate, setExchangeDate] = useState('');
  useEffect(() => {
    const getRates = async () => {

      const response = await http.get('/latest?places=2');
      const data = await response.data;
      //console.log(data);
      let arr = Array.from(Object.keys(data.rates), k => [`${k}`, data.rates[k]]);
      //console.log(arr);
      setRates(arr);
      setLoading(false);
      setBaseCurrency(data.base);
      setExchangeDate(data.date);
    }
    getRates();

  }, [])
  return (
    <>
      <h1>Daily Exchange Rates</h1>
      <div className='text-bold'>
        <span className='float-start'><strong>Base Currency: {baseCurrency}</strong></span>
        <span className='float-end'><strong>Date: {exchangeDate}</strong></span>
        <br />
      </div>
      <ExchangeRate rates={rates} loading={loading} />
    </>

  )
}

export default CurrencyRates
Enter fullscreen mode Exit fullscreen mode

Now we create some components in src/components folder. First is to initlize the axios with base url. I am using "exchangerate.host" website API's for currency services app.

components/CustomAxios.js

import axios from "axios";
const http = axios.create({
    baseURL: 'https://api.exchangerate.host'
});

export default http;
Enter fullscreen mode Exit fullscreen mode

Next we create ExchangeRate.jsx file to show all rates from api.

components/ExchangeRate.jsx

import React from 'react'
import Spinner from './shared/Spinner';

function ExchangeRate({ rates, loading }) {
    if (loading) {
        return <Spinner/>;
    }
    const listing = rates.length > 0 && (
        <div className="row row-cols-5 mt-3">
            {rates.map(item => (
                <div className="col mt-3 mb-1" key={item[0]}>
                    <div className="card bg-dark">
                        <div className="card-body">
                            <div className="card-text text-white">
                                {item[0]} - {item[1]}
                            </div>
                        </div>
                    </div>
                </div>
            ))}
        </div>
    )
    return (
        <>

            {listing}
        </>
    );

}

export default ExchangeRate
Enter fullscreen mode Exit fullscreen mode

Now to load navigation and spinner, create new folder in src/components named "shared" and in src named src/assets. For loader image, you can use any image name in src/assets folder. I've used 'loadeing.gif' name. Update your image name in spinner component

components/shared/Spinner.jsx

import React from 'react'
import spinner from "../../assets/loading.gif";
function Spinner() {
    return (
        <div className='loadingSpinnerContainer'>
            <img src={spinner} alt="Loading..." width="200px" height="200px" />
        </div>
    )
}

export default Spinner
Enter fullscreen mode Exit fullscreen mode

components/shared/Navigation.jsx

import React from 'react'
import { Container, Nav, Navbar, NavDropdown } from 'react-bootstrap'
import { NavLink } from 'react-router-dom'

function Navigation() {
    return (
        <Navbar bg="dark"  variant="dark" expand="lg">
            <Container>
                <Navbar.Brand href="#home">Currency Services</Navbar.Brand>
                <Navbar.Toggle aria-controls="basic-navbar-nav" />
                <Navbar.Collapse id="basic-navbar-nav">
                    <Nav className="me-auto">
                        <NavLink className="nav-link" to="/">Home</NavLink>
                        <NavLink className="nav-link" to="/currency-converter">Currency Converter</NavLink>
                        {/**
                        <NavDropdown title="Dropdown" id="basic-nav-dropdown">
                            <NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item>
                            <NavDropdown.Item href="#action/3.2">Another action</NavDropdown.Item>
                            <NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item>
                            <NavDropdown.Divider />
                            <NavDropdown.Item href="#action/3.4">Separated link</NavDropdown.Item>
                        </NavDropdown> 
                        */}
                    </Nav>
                </Navbar.Collapse>
            </Container>
        </Navbar>

    )
}

export default Navigation
Enter fullscreen mode Exit fullscreen mode

You can get the full source code at following location.

https://bitbucket.org/deepaksinghkushwah/currencyservices/src/master/

Hope you like this app.

Oldest comments (0)