rgl/concurrency/src/Exchanger.tsx
2021-10-04 17:56:09 -05:00

105 lines
3.8 KiB
TypeScript

import React, { useEffect, useRef, useState } from "react";
import axios from "axios";
import Rates from "./Rates";
function Exchanger() {
let [rates, setRates] = useState({} as Rates);
let [inputOne, setInputOne] = useState("");
let [selectedOne, setSelectedOne] = useState("USD");
let [selectedTwo, setSelectedTwo] = useState("EUR");
let [result, setResult] = useState("0.00");
const isFirstRender = useRef(true);
useEffect(() => {
const saved = Number(localStorage.getItem("saved"));
if (Math.floor(Date.now() / 1000) - saved > 86400) {
// If it's been more tha 24 hours, fetch new rates
// The timeout is 24 hours because that's how often the exchangerate-api refreshes its data
axios.get(process.env.API_URL).then(resp => {
setRates(resp.data);
localStorage.setItem("saved", Math.floor(Date.now() / 1000).toString()); // convert milliseconds to seconds
localStorage.setItem("rates", JSON.stringify(resp.data));
});
} else {
// Otherwise, just grab them from localstorage
const data = JSON.parse(localStorage.getItem("rates"));
setRates(data);
}
}, []);
function handleSelectedOne(event: any) {
setSelectedOne(event.target.value);
}
function handleInputOne(event: any) {
setInputOne(event.target.value);
}
function handleSelectedTwo(event: any) {
setSelectedTwo(event.target.value);
}
useEffect(() => {
if (isFirstRender.current) {
// Do nothing if it's the first render
// This will ensure this effect will only fire on state updates, not the initial state
isFirstRender.current = false;
return;
}
let inp = Number(inputOne);
if (Number.isNaN(inp)) {
alert("Input must be a number!");
return;
}
let inpCurrency = selectedOne;
let outCurrency = selectedTwo;
// First, convert the input to USD
let exchange_rate = rates.conversion_rates[inpCurrency];
let usd = inp / exchange_rate;
// Then, convert the USD to the chosen output currency
exchange_rate = rates.conversion_rates[outCurrency];
setResult((usd * exchange_rate).toFixed(2)); // Round to 2 decimal places
}, [rates, inputOne, selectedOne, selectedTwo]); // Only run this effect if one of these changed
function handleSwapClick() {
const temp1 = selectedOne;
setSelectedOne(selectedTwo);
setSelectedTwo(temp1);
const temp2 = inputOne;
setInputOne(result);
setResult(temp2);
}
if (Object.keys(rates).length) {
return <>
<div className="grid-container">
<input type="text" value={inputOne} placeholder="0" onChange={handleInputOne} />
<input type="text" value={result} readOnly />
<select value={selectedOne} onChange={handleSelectedOne}>
{/* Map currency names to dropdown values */}
{Object.keys(rates.conversion_rates).sort((a, b) => a.localeCompare(b)).map((currency, index) => <option key={index}>{currency}</option>)}
</select>
<select value={selectedTwo} onChange={handleSelectedTwo}>
{Object.keys(rates.conversion_rates).sort((a, b) => a.localeCompare(b)).map((currency, index) => <option key={index}>{currency}</option>)}
</select>
</div>
<br />
<div style={{ display: "flex", justifyContent: "center" }}>
<button onClick={handleSwapClick}>Swap</button>
</div>
</>
} else {
return <h1>Loading...</h1>
}
}
export default Exchanger;