Jake11
Jake11

Reputation: 831

React how to make state change synchronous

I am doing currency calculator with react.js

fetching current rates to state by getRates function:

 getRates(currencyShortcut){
     fetch('https://api.fixer.io/latest?base='+currencyShortcut)
       .then(response => response.json())
       .then(parsedJson => this.setState({currencyRates:parsedJson}))
       .catch(error => console.log('fetch error - ', error))   
 }

and I use them to calculate amount for given input value, which is displayed on screen here is how:

calculateCurrenciesValues(curr,userInput){
  let currency1val,currency2val;
  const rate = this.state.currencyRates.rates[this.state.currency2.shortcut];
  if(curr === 1){
    currency1val = userInput;
    currency2val = Math.round(rate*userInput*100)/100;
  }else{
    currency1val = Math.round(userInput/rate*100)/100;
    currency2val = userInput;
  }
  this.setState({
    currenciesValues:[currency1val,currency2val]
  })
}

first HTTP request is done with componentWillMount

componentWillMount(){
    this.getRates('USD');
}

but you can also select another currency from list and than state needs to get new rates, so here is handler for currency changing:

  changeCurrency(currFlag,currShortcut,currSymbol){
    const selectedCurrency = {
      path:currFlag,
      shortcut:currShortcut,
      symbol:currSymbol
    };
    const {listOfCurrencies,currency1,currency2,currenciesValues,currencyRates} = this.state; 

    if(this.state.listOfCurrencies.displayFor === 1 ){
      this.getRates(currShortcut);
      this.setState({
        currency1:selectedCurrency
      })
      this.calculateCurrenciesValues(1,currenciesValues[0])
    }
    else{
      this.setState({
        currency2:selectedCurrency
      });
      this.calculateCurrenciesValues(2,currenciesValues[1])
    }

    this.setState({
      listOfCurrencies:{
        display:!listOfCurrencies.display
      }
    })
  }

As you can see, what I am trying to do is recalculation after currency is changed and although this.getRates(currShortcut) loads new rates for state, calculation this.calculateCurrenciesValues(1,currenciesValues[0]) is done on old rates not fresh ones, as I would expect.

In practice, it looks as follows e.g. I got choosen USD => GBP for default and I enter 10 in first input and get displayed "10$ = 7.06£", so I want something else for USD so I change it for EUR and I got EUR => GBP than I get new rates in state, but calculation is done with old rates so only currency sign changes in displayed values "10€ = 7.06£" and if I am going to change currency for second time lets make it JPY now, there are new rates in state but again calculation is done with old rates, which were correct for EUR "10¥ = 8.79£"

Anybody got an idea, how to make calculation with freshly acquired data?

Upvotes: 1

Views: 723

Answers (3)

Hoàng Trần
Hoàng Trần

Reputation: 103

Your ajax request must be done before you call a method. You can try this

getRates(currencyShortcut, callback){
     fetch('https://api.fixer.io/latest?base='+currencyShortcut)
       .then(response => response.json())
       .then(parsedJson => this.setState({currencyRates:parsedJson}, function(){if(callback){callback();})
       .catch(error => console.log('fetch error - ', error))   
 }

 if(this.state.listOfCurrencies.displayFor === 1 ){
  this.getRates(currShortcut, function(){
      this.setState({
        currency1:selectedCurrency
      }, function(){
        this.calculateCurrenciesValues(1,currenciesValues[0])
      })
  });
}

Upvotes: 0

Logan Shoemaker
Logan Shoemaker

Reputation: 4497

If your calculateCurrenciesValues() is using an incorrect value, I would first guess you are setting state and calling functions in the wrong order.

I would approach it as follows...

  • User enters value into input field
  • OnChange event happens where state is updated to reflect the current input value
  • User changes the currency

  • Event fires where function runs to calculate new value based on the current state (which reflects the current user input)

Sounds like you may be handling events out of order. Look into your code to make sure your state is first being updated to reflect the current user input and base your calculations on that current state. Hope this helps, sorry if not.

Upvotes: 0

thedude
thedude

Reputation: 9812

Try to store in the state only the rates and the current user selections.

Perform the calculation in your render method, this way it will rerun whenever the state changes.

Upvotes: 4

Related Questions