React LogoCurrency Converter

A Currency Converter is an application or tool that allows users to convert an amount from one currency into another, based on current or historical exchange rates. Its primary purpose is to help individuals and businesses understand the equivalent value of money across different economies.

Core Functionalities and Concepts:

1. Input Amount: Users specify the numerical amount they wish to convert.
2. Source Currency Selection: Users choose the original currency of the input amount (e.g., USD, EUR).
3. Target Currency Selection: Users select the currency they want to convert to (e.g., JPY, GBP).
4. Exchange Rate Retrieval: This is the most critical part. The converter needs access to real-time or near real-time exchange rates. This is typically achieved by integrating with external APIs (Application Programming Interfaces) provided by financial data services. These APIs deliver the conversion rate between two specific currencies.
5. Conversion Logic: Once the amount, source currency, target currency, and the exchange rate are known, the conversion is a simple multiplication: `Converted Amount = Input Amount × Exchange Rate (from source to target)`.
6. Display Result: The calculated converted amount is then displayed to the user, often formatted with the appropriate currency symbol and decimal places.
7. Error Handling & Loading States: A robust converter should handle cases where API calls fail, exchange rates cannot be fetched, or invalid inputs are provided. It should also indicate when data is being loaded.

How it Works (Conceptual Flow):

* The user opens the application and sees input fields for the amount and dropdowns for selecting 'from' and 'to' currencies.
* They enter an amount (e.g., 100) and select 'USD' as the 'from' currency and 'EUR' as the 'to' currency.
* The application makes an API call to a currency exchange service to get the current rate of USD to EUR.
* If the API returns, say, `1 USD = 0.92 EUR`, the application calculates `100 * 0.92 = 92`.
* The result, '92 EUR', is displayed to the user.

Technology Stack for Web (e.g., React):

* Frontend Framework: React (or Angular, Vue.js) for building the user interface and managing component states.
* State Management: `useState` and `useEffect` hooks in React are ideal for managing input values, selected currencies, fetched rates, and the converted result.
* API Calls: The built-in `fetch` API or libraries like Axios are used to make HTTP requests to external exchange rate APIs.
* Styling: CSS or CSS-in-JS libraries for making the converter visually appealing.

Building a currency converter is an excellent exercise for learning about API integration, asynchronous data handling, state management, and basic user interface design.

Example Code

import React, { useState, useEffect } from 'react';

function CurrencyConverter() {
  const [amount, setAmount] = useState(1);
  const [fromCurrency, setFromCurrency] = useState('USD');
  const [toCurrency, setToCurrency] = useState('EUR');
  const [convertedAmount, setConvertedAmount] = useState(null);
  const [exchangeRate, setExchangeRate] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  // A simplified list of currencies for the dropdowns
  const currencies = ['USD', 'EUR', 'GBP', 'JPY', 'CAD', 'AUD', 'CHF', 'CNY', 'SEK', 'NZD'];

  // --- Mock API Data and Function for Demonstration ---
  // In a real application, you would use a service like exchangerate-api.com, openexchangerates.org, etc.
  // This mock simulates an API call and its response.
  const MOCK_EXCHANGE_RATES = {
    "USD": { "EUR": 0.92, "GBP": 0.79, "JPY": 156.70, "CAD": 1.37, "AUD": 1.51, "CHF": 0.90, "CNY": 7.23, "SEK": 10.92, "NZD": 1.63, "USD": 1 },
    "EUR": { "USD": 1.09, "GBP": 0.86, "JPY": 170.80, "CAD": 1.49, "AUD": 1.64, "CHF": 0.98, "CNY": 7.87, "SEK": 11.90, "NZD": 1.77, "EUR": 1 },
    "GBP": { "USD": 1.27, "EUR": 1.16, "JPY": 198.00, "CAD": 1.73, "AUD": 1.90, "CHF": 1.13, "CNY": 9.12, "SEK": 13.78, "NZD": 2.06, "GBP": 1 },
    "JPY": { "USD": 0.0064, "EUR": 0.0058, "GBP": 0.0051, "CAD": 0.0087, "AUD": 0.0096, "CHF": 0.0057, "CNY": 0.046, "SEK": 0.069, "NZD": 0.010, "JPY": 1 },
    "CAD": { "USD": 0.73, "EUR": 0.67, "GBP": 0.58, "JPY": 114.60, "AUD": 1.10, "CHF": 0.66, "CNY": 5.29, "SEK": 7.98, "NZD": 1.19, "CAD": 1 }
    // ... and so on for other currencies
  };

  const fetchExchangeRate = (from, to) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => { // Simulate network delay
        if (MOCK_EXCHANGE_RATES[from] && MOCK_EXCHANGE_RATES[from][to]) {
          resolve({ conversion_rate: MOCK_EXCHANGE_RATES[from][to] });
        } else {
          reject(new Error(`Exchange rate not found for ${from} to ${to}.`));
        }
      }, 500); 
    });
  };
  // --- End Mock API Section ---

  useEffect(() => {
    const getExchangeRate = async () => {
      if (fromCurrency === toCurrency) {
        setExchangeRate(1);
        setConvertedAmount(amount);
        setLoading(false);
        return;
      }

      setLoading(true);
      setError(null);
      setConvertedAmount(null);

      try {
        // In a real app, replace with a call to a real currency API:
        // const response = await fetch(`https://api.example.com/latest?from=${fromCurrency}&to=${toCurrency}&apikey=YOUR_API_KEY`);
        // if (!response.ok) throw new Error('Failed to fetch exchange rate');
        // const data = await response.json();
        // setExchangeRate(data.conversion_rate); 

        // Using the mock API for this example
        const data = await fetchExchangeRate(fromCurrency, toCurrency);
        setExchangeRate(data.conversion_rate);

      } catch (err) {
        setError(err.message);
        setExchangeRate(null);
      } finally {
        setLoading(false);
      }
    };

    getExchangeRate();
  }, [fromCurrency, toCurrency]); // Re-fetch rate when currencies change

  useEffect(() => {
    if (exchangeRate !== null && amount !== null) {
      setConvertedAmount((amount * exchangeRate).toFixed(2));
    } else {
      setConvertedAmount(null);
    }
  }, [amount, exchangeRate]); // Re-calculate when amount or rate changes

  const handleAmountChange = (e) => {
    const value = parseFloat(e.target.value);
    setAmount(isNaN(value) ? 0 : value);
  };

  const currencyConverterStyles = {
    fontFamily: 'Arial, sans-serif',
    maxWidth: '400px',
    margin: '50px auto',
    padding: '20px',
    border: '1px solid #ddd',
    borderRadius: '8px',
    boxShadow: '0 2px 5px rgba(0,0,0,0.1)'
  };

  const inputGroupStyles = {
    marginBottom: '15px'
  };

  const labelStyles = {
    display: 'block',
    marginBottom: '5px',
    fontWeight: 'bold'
  };

  const selectOrInputStyles = {
    width: '100%',
    padding: '8px',
    margin: '5px 0',
    borderRadius: '4px',
    border: '1px solid #ccc'
  };

  const resultStyles = {
    marginTop: '20px',
    padding: '10px',
    backgroundColor: '#e6ffe6',
    border: '1px solid #cce6cc',
    borderRadius: '4px',
    textAlign: 'center',
    fontWeight: 'bold'
  };

  const errorStyles = {
    color: 'red',
    marginTop: '10px'
  };

  const loadingStyles = {
    color: '#007bff',
    marginTop: '10px'
  };

  return (
    <div style={currencyConverterStyles}>
      <h2>Currency Converter</h2>

      <div style={inputGroupStyles}>
        <label htmlFor="amount" style={labelStyles}>Amount:</label>
        <input
          id="amount"
          type="number"
          value={amount}
          onChange={handleAmountChange}
          style={selectOrInputStyles}
          step="0.01"
          min="0"
        />
      </div>

      <div style={inputGroupStyles}>
        <label htmlFor="fromCurrency" style={labelStyles}>From Currency:</label>
        <select
          id="fromCurrency"
          value={fromCurrency}
          onChange={(e) => setFromCurrency(e.target.value)}
          style={selectOrInputStyles}
        >
          {currencies.map((currency) => (
            <option key={currency} value={currency}>
              {currency}
            </option>
          ))}
        </select>
      </div>

      <div style={inputGroupStyles}>
        <label htmlFor="toCurrency" style={labelStyles}>To Currency:</label>
        <select
          id="toCurrency"
          value={toCurrency}
          onChange={(e) => setToCurrency(e.target.value)}
          style={selectOrInputStyles}
        >
          {currencies.map((currency) => (
            <option key={currency} value={currency}>
              {currency}
            </option>
          ))}
        </select>
      </div>

      {loading && <p style={loadingStyles}>Loading exchange rate...</p>}
      {error && <p style={errorStyles}>Error: {error}</p>}

      {!loading && !error && convertedAmount !== null && (
        <div style={resultStyles}>
          <p>{amount} {fromCurrency} = <strong>{convertedAmount} {toCurrency}</strong></p>
          {exchangeRate && <small>1 {fromCurrency} = {exchangeRate.toFixed(4)} {toCurrency}</small>}
        </div>
      )}

      {!loading && !error && convertedAmount === null && exchangeRate === null && (
        <p style={{textAlign: 'center', color: '#666'}}>Enter an amount and select currencies to convert.</p>
      )}
    </div>
  );
}

export default CurrencyConverter;