Build a Currency Converter - Build a Currency Converter

Tell us what’s happening:

My code is failing tests 4 (select options not matching USD, EUR, etc..), 6 (value of second select element being recalculated) and 9 (formatting not matching XX.XX CCC). I’m not sure why these are failing. Any help would be appreciated!

Your code so far

<!-- file: index.html -->
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <title>Currency Converter</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
    <script
      data-plugins="transform-modules-umd"
      type="text/babel"
      src="index.jsx"
    ></script>
    <link rel="stylesheet" href="styles.css" />
</head>

<body>
    <div id="root"></div>
    <script
      data-plugins="transform-modules-umd"
      type="text/babel"
      data-presets="react"
      data-type="module"
    >
      import { CurrencyConverter } from './index.jsx';
      ReactDOM.createRoot(document.getElementById('root')).render(<CurrencyConverter />);
    </script>
</body>

</html>
/* file: styles.css */
* {
  font-family: Helvetica;
  color: white;
}

body {
  background-color: rgb(52, 52, 52);
}

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 75%;
  border-radius: 10px;
  max-width: 600px;
  min-width: 200px;
  margin-left: auto;
  margin-right: auto;
  background-color: rgb(69, 101, 76);
  padding: 20px;
}

h1 {
  margin: 0px;
  text-align: center;
}

p {
  margin-bottom: 5px;
}

input {
  all: unset;
  height: 25px;
  padding-left: 10px;
}

select {
  all: unset;
  padding-top: 4px;
  height: 20px;
  padding-left: 10px;
}

.select-field{
  vertical-align: middle;
  border: 1px solid white;
  border-radius: 5px;
  width: 50%;
  margin-bottom: 15px;
}

.total {
  font-weight: bold;
  font-size: 18px;
  color: rgb(60, 219, 60);
}

.reset {
  cursor: pointer;
  margin-top: 15px;
  font-size: 16px;
  height: 30px;
  width: 30%;
  border-radius: 10px;
  border: none;
  background-color: rgb(26, 57, 26);
}
/* file: index.jsx */
const { useState, useMemo, useEffect, useRef, useCallback } = React;

export function CurrencyConverter() {

const [currency, setCurrency] = useState(1);
const [startRate, setStartRate] = useState(1);
const [targetRate, setTargetRate] = useState(1);
const [amount, setAmount] = useState(0);
const [startUnits, setStartUnits] = useState("USD")
const [targetUnits, setTargetUnits] = useState("USD")

const defaultStartUnit = useRef("USD")
const defaultTargetUnit = useRef("USD")

const currencyOptions = [
  {
    name: "USD",
    rate: 1
  },
  {
    name: "EUR",
    rate: 0.85
  },
  {
    name: "GBP",
    rate: 0.73
  },
  {
    name: "JPY",
    rate: 144.3
  }, 
  {
    name: "CAD",
    rate: 1.36
  }
]

const total = useMemo(() => {
    return currency * (targetRate / startRate);
  }, [currency, startRate, targetRate]);

const handleStartUnits = useCallback((e) => {
  const value = e.target.value;
  const unit = e.target.options[e.target.selectedIndex].text;
  setStartRate(value);
  setStartUnits(unit);
})

const handleTargetExchange = useCallback((e) => {
  const value = e.target.value;
  const unit = e.target.options[e.target.selectedIndex].text;
  setTargetRate(value);
  setTargetUnits(unit)
});

const resetAll = useCallback(() => {
  setCurrency(1)
  setTargetRate(1)
  setStartRate(1)
  setTargetUnits("USD")
  defaultStartUnit.current.selectedIndex = 0;
  defaultTargetUnit.current.selectedIndex = 0;
});


  return (
    <div className="container">
    <h1>Currency Converter</h1>
    <p>Convert {startUnits} to {targetUnits}</p>
    <input className="select-field" type="number" value={currency} onChange={e=>setCurrency(e.target.value)}/>
    <p>Start Currency</p>
    <select className="select-field" onChange={handleStartUnits} ref={defaultStartUnit}>
      {currencyOptions.map((c)=>(
        <option value={c.rate} key={c.name}>{c.name}</option>
      ))}
    </select>
    <p>Target Currency</p>
    <select className="select-field" onChange={handleTargetExchange} ref={defaultTargetUnit}>
      {currencyOptions.map((c)=>(
        <option value={c.rate} key={c.name}>{c.name}</option>
      ))}
    </select>
    <p className="total">Converted amount: {total.toFixed(2)} {targetUnits}</p>
    <button className="reset" onClick={resetAll}>Reset</button>
    </div>
  )
}

Your browser information:

User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36

Challenge Information:

Build a Currency Converter - Build a Currency Converter

the reason #4 is failing is because the test checks the value of the option element and is expecting to see USD etc not numerical values as you currently have.

1 Like

I had a feeling that would be the case. Thank you. Any insight into the other errors? I’m very curious about the formatting error. Do you know what it is trying to read?

i didn’t look into any of the others. Once you’ve fixed the first issue, try different tests and see what you can conclude and let us know here in your response.
Please make sure to include the newest version of your code.

Thanks! Assigning the rate to the value solved issues #4 and #9, #6 is still throwing an error. I am using useCallback() for all function returns so I’m not sure what the test is looking for?

const { useState, useMemo, useEffect, useRef, useCallback } = React;

export function CurrencyConverter() {

const [currency, setCurrency] = useState(1);
const [startRate, setStartRate] = useState(1);
const [targetRate, setTargetRate] = useState(1);
const [startUnits, setStartUnits] = useState("USD")
const [targetUnits, setTargetUnits] = useState("USD")

const defaultStartUnit = useRef("USD")
const defaultTargetUnit = useRef("USD")

const currencyOptions = [
  {
    name: "USD",
    rate: 1
  },
  {
    name: "EUR",
    rate: 0.85
  },
  {
    name: "GBP",
    rate: 0.73
  },
  {
    name: "JPY",
    rate: 144.3
  }, 
  {
    name: "CAD",
    rate: 1.36
  }
]

const total = useMemo(() => {
    return currency * (targetRate / startRate);
  }, [currency, startRate, targetRate]);

const handleStartUnits = useCallback((e) => {
  const selectedName = e.target.value
  const currency = currencyOptions.find(c => c.name === selectedName);
  const unit = e.target.options[e.target.selectedIndex].text;
  setStartRate(currency.rate);
  setStartUnits(unit);
})

const handleTargetExchange = useCallback((e) => {
  const selectedName = e.target.value
  const currency = currencyOptions.find(c => c.name === selectedName);
  const unit = e.target.options[e.target.selectedIndex].text;
  setTargetRate(currency.rate);
  setTargetUnits(unit)
});

const resetAll = useCallback(() => {
  setCurrency(1)
  setTargetRate(1)
  setStartRate(1)
  setTargetUnits("USD")
  defaultStartUnit.current.selectedIndex = 0;
  defaultTargetUnit.current.selectedIndex = 0;
});


  return (
    <div className="container">
    <h1>Currency Converter</h1>
    <p>Convert {startUnits} to {targetUnits}</p>
    <input className="select-field" type="number" value={currency} onChange={e=>setCurrency(e.target.value)}/>
    <p>Start Currency</p>
    <select className="select-field" onChange={handleStartUnits} ref={defaultStartUnit}>
      {currencyOptions.map((c)=>(
        <option value={c.name} key={c.name}>{c.name}</option>
      ))}
    </select>
    <p>Target Currency</p>
    <select className="select-field" onChange={handleTargetExchange} ref={defaultTargetUnit}>
      {currencyOptions.map((c)=>(
        <option value={c.name} key={c.name}>{c.name}</option>
      ))}
    </select>
    <p className="total">Converted amount: {total.toFixed(2)} {targetUnits}</p>
    <button className="reset" onClick={resetAll}>Reset</button>
    </div>
  )
}

I’m new to this curriculum but to me it looks like it wants you to memoize the conversion of the currency conversion values of the amount the user typed in.
I think that would mean saving ALL the values for ALL the currencies not just the current one.

In the description to the exercise it says:

Your CurrencyConverter component should memoize the calculation of the converted amounts for the from currency such that a change in the to select option will not recalculate the converted amounts.

Notice how they use plural form and refer to ‘amounts’…

so the way things should work I’m guessing is.
User types in a number
You convert that number into ALL the different types of currencies and memoize that.
If the user changes the FROM field then the calculation should be redone (and rememoized)
If the user changes the TO field then you just use the memoized value.

So right now for example, your useMemo will be triggered too many times because your definition of the dependencies includes the target value too.

ps. it turns out that was the issue. So you just need to focus on fixing the last thing I said…

1 Like

You learned 4 memoization methods, each for a different situation. Are you sure useCallback is the correct one for this case?

Edit: nvm I see you’re using the correct memoization for that function. Just need to focus on when it triggers as @hbar1st mentioned

memoize the calculation of the converted amounts for the from currency such that a change in the to select option will not recalculate the converted amounts.

1 Like

Thanks so much, after a bit of trial and error I was able to see the issue.

2 Likes