1

The below is my code.

What i am doing is a currency converter app by fetching external API. It is guaranteed process.env.REACT_APP_DATA_SOURCE is always correct.

The problem here is: If I change the currency, either inputCurrency or outputCurrency, I need to click "Submit" button twice instead of once to display a correct answer. Why is it the case?

I can't come up with any better wording so I used "delay" in the heading.

Thank you!

import './App.css';
import { useState, useEffect } from 'react';
import { Select, Button, ButtonGroup } from '@chakra-ui/react';

function App() {
  const [exchangeRate, setExchangeRate] = useState(7.8491);

  const [inputCurrency, setInputCurrency] = useState('USD');
  const [inputAmount, setInputAmount] = useState(1);

  const [outputCurrency, setOutputCurrency] = useState('HKD');
  const [outputAmount, setOutputAmount] = useState(7.8491);

  useEffect(() => {
    const fetchData = async (url) => {
      await fetch(url)
        .then((response) => response.json())
        .then((data) => setExchangeRate(data.conversion_rates[outputCurrency]));
    };

    try {
      let url = `${process.env.REACT_APP_DATA_SOURCE}${inputCurrency}`;
      fetchData(url);
    } catch (err) {
      console.log(err);
    }
  }, [inputCurrency, outputCurrency]);

  const handleSubmit = async (e) => {
    e.preventDefault();

    setInputCurrency(e.target.inputCurrency.value);
    setInputAmount(e.target.inputAmount.value);
    setOutputCurrency(e.target.outputCurrency.value);

    let result = inputAmount * exchangeRate;

    setOutputAmount(result);
  };

  return (
    <div className="main-body">
      <form onSubmit={handleSubmit}>
        <h3>Set input amount:</h3>
        <input className="inputAmount" name="inputAmount" type="number" />

        <h3>Set input currency:</h3>
        <Select name="inputCurrency">
          <option value="USD">USD</option>
          <option value="HKD">HKD</option>
          <option value="CNY">CNY</option>
          <option value="GBP">GBP</option>
          <option value="CAD">CAD</option>
        </Select>

        <h3>Set output currency:</h3>
        <Select name="outputCurrency">
          <option value="USD">USD</option>
          <option value="HKD">HKD</option>
          <option value="CNY">CNY</option>
          <option value="GBP">GBP</option>
          <option value="CAD">CAD</option>
        </Select>

        <Button colorScheme="blue" type="submit">
          Submit
        </Button>
      </form>

      <h3>
        {inputAmount} {inputCurrency} is equal to:
      </h3>
      <h3>
        {outputAmount} {outputCurrency}
      </h3>
    </div>
  );
}

export default App;

1 Answer 1

1

You are calculating the output amount at the wrong place. First, you update the inputCurrency and outputCurrency in handleSubmit. This means the useEffect will be 'triggered' and the new exchange rate will be fetched. But that's not gonna happen immediately. Definitely not before result is calculated.

const handleSubmit = async (e) => {
    e.preventDefault();

    setInputCurrency(e.target.inputCurrency.value);
    setInputAmount(e.target.inputAmount.value);
    setOutputCurrency(e.target.outputCurrency.value);

    //This will ALWAYS use the old previous exchange rate.
    let result = inputAmount * exchangeRate;
    setOutputAmount(result);
};

What you want is to recalculate outputAmount whenever the exchangeRate or inputAmount changes. You can use another useEffect like:

useEffect(() => {
    //Take these out from handleSubmit.
    let result = inputAmount * exchangeRate;
    setOutputAmount(result);
}, [inputAmount, exchangeRate])
Sign up to request clarification or add additional context in comments.

2 Comments

love you! I edited my code using your idea and it works! a BIG THANK YOU to you!
accepted, my friend

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.