Photo by Lautaro Andreani on Unsplash
Day 37 & 38- Currency converter with React.
Hi and welcome to days 37 and 38 of my challenge!ππͺ
Currency converter using Frankfurter API
Today, I am developing a simple currency converter application. I have the starter file provided by the course I'm enrolled in. Additionally, I'll be using the API link from Frankfurter to fetch currency exchange rates and perform conversions.
So, I've taken the first step by making the elements controlled. This includes both the input field and the selection of currencies for conversion.
I've followed our three-step process for creating state:
First, I've declared three state variables: amount
, fromCurrency
, and toCurrency
, initializing each with specific values and corresponding update functions (setAmount
, setFromCurrency
, and setToCurrency
). The initial values provided to useState
represent the default states for these variables. For instance, the amount
is set to 1, fromCurrency
to 'EUR'
(Euro), and toCurrency
to 'USD'
(United States Dollar)
So, moving on to the second step, we utilize these state variables within our JSX. This integration ensures that the values of input fields and select dropdowns are controlled dynamically by the application.
Specifically:
The
value
attribute of the input field is set to theamount
state variable. This setup guarantees that the input field always reflects the current value ofamount
.Similarly, the
value
attribute of each select element is set to the respective state variable (fromCurrency
andtoCurrency
). Consequently, the dropdowns consistently display the currently selected currencies.
Now, advancing to the third step, we implement the use of updater functions to modify the state variables.
The onChange
event handlers of the input field and select dropdowns are assigned to functions. These functions, when triggered by user input or selection changes, invoke the corresponding updater functions. This mechanism ensures that the state variables (amount
, fromCurrency
, and toCurrency
) are updated dynamically in response to user interactions, thereby maintaining the controlled behavior of the application.
Second and third step togetherπ
So next, I'll create the useEffect
hook with an empty dependency array, ensuring it only runs once during the initial render, triggered upon component mount.
We'll make an HTTP request to a specific URL. Therefore, we need a fetch function, which returns a promise. Hence, we now need an async
function.
Since the effect function itself cannot be async
, let's create another one here. We'll define an async
function and call it simply 'convert' and then call the function.
We'll perform our fetch request to this URL.πLine 13.
Let's grab that. Of course, we need to replace all these placeholders in this template with actual values.
So, 'amount', 'fromCurrency'
, and 'toCurrency
' will be substituted accordingly.ππ
By logging that to the console, we can see its format.
Immediately, we observe that we receive a result. Thus, our effect is already working with the default data provided.ππ
Now, I need to create a new state variable called converted
. This variable is used to store the result of the currency conversion. The converted value will be rendered within a paragraph tag in the JSX.
The setConverted
function is used to update the converted
state variable. It takes an argument, which in this case is data.rates[toCurrency]
, representing the converted amount fetched from the API response.
Here, data.rates[toCurrency]
accesses the conversion rate from the data.rates
object using toCurrency
as the key. This gives us the conversion rate for the specified toCurrency
.
For instance, if toCurrency
is set to "USD"
, then data.rates["USD"]
retrieves the conversion rate for converting to US dollars.
Once setConverted(data.rates[toCurrency])
is called, it updates the converted
state variable with the retrieved conversion rate, representing the converted amount. This conversion rate is then rendered in the UI to display the converted value at Line 48ππ.ππ
Result π
Now, if I update this to 100, nothing happens.
As we type a new number here, we update the state inside the App
component, triggering a re-render of this component. However, our effect, at present, only runs during the initial render. Since we haven't specified anything in our dependency array, React doesn't know that it should also re-run this effect when the state updates.ππ
Let's change thatπ. Let's give our dependency array all the values that our effect depends on. So, 'amount'
, 'fromCurrency'
, and 'toCurrency'
.π
You might notice that it takes some time for the update to happen and that the change wasn't immediate. If you have a slower internet connection, it may take even longer because our application needs to make this API call, and only after that, the state is updated. So, to tell the user about that, I'll add a loading state.
isLoading
is a state variable initialized using the useState
hook. It represents whether the conversion operation is currently in progress (true
) or not (false
). setIsLoading
is the corresponding function to update the value of isLoading
.
In the useEffect
hook, setIsLoading(true)
is called before starting the conversion operation to indicate that loading is in progress. Then, once the conversion operation is completed and the converted value is set, setIsLoading(false)
is called to indicate that loading has finished.
The UI conditionally renders either a loading message (<p>converting...</p>
) if isLoading
is true
, or the converted value and the target currency if isLoading
is false
.ππ
Now, I'll disable all the input and select elements when isLoading
is true
, you can conditionally set the disabled
attribute based on the value of isLoading
.ππ
Next, there's an error when we try to convert from one currency to the same currency. For instance, if we attempt to convert USD
to USD
, we encounter an error.π
So to fix this
If the source currency (
fromCurrency
) is equal to the target currency (toCurrency
), it means that no conversion is needed because both currencies are the same.In such a case, instead of fetching data from the API and performing a conversion, the converted value can be set directly to the same value as the input amount (
amount
). This is essentially saying that 100 units of EUR is equivalent to 100 units of EUR.setConverted(amount)
sets theconverted
state variable to the same value as the input amount (amount
), effectively bypassing the API call and directly displaying the input amount in the UI.ππ
Remember that we ignored errors here, so we didn't handle them at all. But you should never do that in the real world. That's just something I wanted to mention here. But since this is just a learning exercise, that's no big deal.
Then one final step i took was to remove the disabled = {isLoading}
for only the input field that i used before as i had some issues with it.
Currency converter using Open Exchange rate API
So, I decided to convert USD to NGN, but I encountered a 404 error. Afterward, I searched for another API link that supports the Nigerian Naira among other currencies and found the Open Exchange Rate API.
I copied everything I had already done and decided to try it with the new API link. However, I needed to sign up for the free version and obtain my app ID to be able to make fetch requests.
First, I made some modifications. The currencyConverter
component is for the Frankfurt API, while the ExchangeRate
component utilizes the Open Exchange Rate API.ππ
So, here I logged the data of both to the console to see the difference.ππ
So, this is what I did differently in one picture for the ExchangeRate
component:
Lines 15 and 19 π. This works exactly like the first but supports more currencies.
To calculate the converted amount, the component multiplies the provided amount
(which is in the fromCurrency
) with the conversion rate obtained from the API response. This calculation yields the equivalent amount in the toCurrency
.
converted = amount * conversionRate
For example, if amount
is 100 and the conversion rate is 1.25 (indicating that 1 unit of fromCurrency
is equivalent to 1.25 units of toCurrency
), then the converted amount would be:
converted = 100 * 1.25 = 125
fromCurrency
and calculates the conversion based on it. Additionally, the Open Exchange Rates API requires an API key for accessing the data, whereas the Frankfurter API does not.Thank you for reading.π