1
\$\begingroup\$

I'm making a currency exchange app, it works as follows:

User types in on desired currency input and the rest of the input fields change with state based on the currency value from the input used, I'm providing the main component (container), but this codesandbox has a working demo.

  1. I need some help or guidance on how to setState in a cleaner way probably with a loop, map or a new function.
  2. Any help on how to create my CURRENCIES in a better way, considering that maybe someday the API I'm using will add an extra currency, and right now I would have to add it manually.
  3. Considering point #2 mentioned above, I guess I will also need to improve my switch statement to adapt to the API growing in currencies.

This is a working codesandbox.

import React, { Component } from "react";
import { render } from "react-dom";

import SingleInput from "./SingleInput.js";

import fetchedCurrencies from "./data.json";

const CURRENCIES = [
  { name: "MXN" },
  { name: "USD", value: fetchedCurrencies[0]["bm:Obs"][0].$.OBS_VALUE },
  { name: "USDFix", value: fetchedCurrencies[1]["bm:Obs"][0].$.OBS_VALUE },
  { name: "EUR", value: fetchedCurrencies[2]["bm:Obs"][0].$.OBS_VALUE },
  { name: "CAD", value: fetchedCurrencies[3]["bm:Obs"][0].$.OBS_VALUE },
  { name: "JPY", value: fetchedCurrencies[4]["bm:Obs"][0].$.OBS_VALUE },
  { name: "GBP", value: fetchedCurrencies[5]["bm:Obs"][0].$.OBS_VALUE }
];

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      MXN: "",
      USD: "",
      USDFix: "",
      EUR: "",
      CAD: "",
      JPY: "",
      GBP: ""
    };
  }

  handleCurrencyInput = event => {
    const { name, value } = event.target;
    switch (name) {
      case "USD":
        this.setState({
          MXN: value * CURRENCIES[1].value,
          USD: value,
          USDFix: value * CURRENCIES[1].value / CURRENCIES[2].value,
          EUR: value * CURRENCIES[1].value / CURRENCIES[3].value,
          CAD: value * CURRENCIES[1].value / CURRENCIES[4].value,
          JPY: value * CURRENCIES[1].value / CURRENCIES[5].value,
          GBP: value * CURRENCIES[1].value / CURRENCIES[6].value
        });
        break;

      case "USDFix":
        this.setState({
          MXN: value * CURRENCIES[2].value,
          USD: value * CURRENCIES[2].value / CURRENCIES[1].value,
          USDFix: value,
          EUR: value * CURRENCIES[2].value / CURRENCIES[3].value,
          CAD: value * CURRENCIES[2].value / CURRENCIES[4].value,
          JPY: value * CURRENCIES[2].value / CURRENCIES[5].value,
          GBP: value * CURRENCIES[2].value / CURRENCIES[6].value
        });
        break;

      case "EUR":
        this.setState({
          MXN: value * CURRENCIES[3].value,
          USD: value * CURRENCIES[3].value / CURRENCIES[1].value,
          USDFix: value * CURRENCIES[3].value / CURRENCIES[2].value,
          EUR: value,
          CAD: value * CURRENCIES[3].value / CURRENCIES[4].value,
          JPY: value * CURRENCIES[3].value / CURRENCIES[5].value,
          GBP: value * CURRENCIES[3].value / CURRENCIES[6].value
        });
        break;

      case "CAD":
        this.setState({
          MXN: value * CURRENCIES[4].value,
          USD: value * CURRENCIES[4].value / CURRENCIES[1].value,
          USDFix: value * CURRENCIES[4].value / CURRENCIES[2].value,
          EUR: value * CURRENCIES[4].value / CURRENCIES[3].value,
          CAD: value,
          JPY: value * CURRENCIES[4].value / CURRENCIES[5].value,
          GBP: value * CURRENCIES[4].value / CURRENCIES[6].value
        });
        break;

      case "JPY":
        this.setState({
          MXN: value * CURRENCIES[5].value,
          USD: value * CURRENCIES[5].value / CURRENCIES[1].value,
          USDFix: value * CURRENCIES[5].value / CURRENCIES[2].value,
          EUR: value * CURRENCIES[5].value / CURRENCIES[3].value,
          CAD: value * CURRENCIES[5].value / CURRENCIES[4].value,
          JPY: value,
          GBP: value * CURRENCIES[5].value / CURRENCIES[6].value
        });
        break;

      case "GBP":
        this.setState({
          MXN: value * CURRENCIES[6].value,
          USD: value * CURRENCIES[6].value / CURRENCIES[1].value,
          USDFix: value * CURRENCIES[6].value / CURRENCIES[2].value,
          EUR: value * CURRENCIES[6].value / CURRENCIES[3].value,
          CAD: value * CURRENCIES[6].value / CURRENCIES[4].value,
          JPY: value * CURRENCIES[6].value / CURRENCIES[5].value,
          GBP: value
        });
        break;

      default:
        this.setState({
          MXN: value,
          USD: value / CURRENCIES[1].value,
          USDFix: value / CURRENCIES[2].value,
          EUR: value / CURRENCIES[3].value,
          CAD: value / CURRENCIES[4].value,
          JPY: value / CURRENCIES[5].value,
          GBP: value / CURRENCIES[6].value
        });
    }
  };

  render() {
    return (
      <div>
        {CURRENCIES.map(currency => (
          <SingleInput
            type="text"
            maxLength={12}
            key={currency.name}
            name={currency.name}
            placeholder={`${currency.name} ${currency.value || ""}` || "MXN"}
            value={this.state[`${currency.name}`] || ""}
            onChange={this.handleCurrencyInput}
          />
        ))}
      </div>
    );
  }
}

export default App;
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

In handleCurrencyInput the formula is always converted = value * inputcurrencyrate / outputcurrencyrate;

The two special cases MXN, and identity (eg USD -> USD) still follow the equation if you set MXN to 1

So if you structure CURRENCIES as a dict instead of an array you can simplify to

const CURRENCIES = {
  MXN: 1,
  USD: fetchedCurrencies[0]["bm:Obs"][0].$.OBS_VALUE,
  USDFix: fetchedCurrencies[1]["bm:Obs"][0].$.OBS_VALUE,
  EUR: fetchedCurrencies[2]["bm:Obs"][0].$.OBS_VALUE,
  CAD: fetchedCurrencies[3]["bm:Obs"][0].$.OBS_VALUE,
  JPY: fetchedCurrencies[4]["bm:Obs"][0].$.OBS_VALUE,
  GBP: fetchedCurrencies[5]["bm:Obs"][0].$.OBS_VALUE
};

...

handleCurrencyInput = event => {
    const { name, value } = event.target;
    const newState = {};
    for (const currency in CURRENCIES) {
        this.setState({[currency]: value * (CURRENCIES[name] / CURRENCIES[currency])})
    }
}

And adding additional currencies is trivial

Adapted render:

render() {
  return (
    <div>
      {Object.keys(CURRENCIES).map(currency => (
        <SingleInput
          type="text"
          maxLength={12}
          key={currency}
          name={currency}
          placeholder={`${currency} ${CURRENCIES[currency] || ""}` || "MXN"}
          value={this.state[`${currency}`] || ""}
          onChange={this.handleCurrencyInput}
        />
      ))}
    </div>
  );
}
\$\endgroup\$
7
  • \$\begingroup\$ Is a dict basically an object? or what is the difference? \$\endgroup\$ May 3, 2018 at 2:28
  • 1
    \$\begingroup\$ Yes, just an simple object \$\endgroup\$ May 3, 2018 at 2:31
  • \$\begingroup\$ So I encountered a problem with the way I'm using .map() on CURRENCIES if you see the codebox example you can see what I'm talking about, any ideas how to solve this considering that? I know i can use Object.keys(CURRENCIES).map() but it doesn't let me input decimal points wouldn't know if it's related though, just an observation. \$\endgroup\$ May 3, 2018 at 3:50
  • \$\begingroup\$ I added an updated render, I'm not sure what decimal point issue you're encountering. \$\endgroup\$ May 3, 2018 at 3:55
  • \$\begingroup\$ So there are two cases: Trying to use a decimal point as in 15.50 and also numbers like 15 triggers some kind of bug on currencies different from MXN. I adapted the sandbox with your code and it replicates the problem codesandbox \$\endgroup\$ May 3, 2018 at 4:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.