My crypto calculator should change the opposite field (Amount in, Amount out) based on
API calls. For example, if I choose Ethereum – Bitcoin pair
and I enter 1 in Ethereum field, I will get 0.06902 in Bitcoin field.
If I enter 1 in the Bitcoin field, I get 14,49 in the Ethereum field (real rates).
And my calculator does do that, but I can’t see the change right away. I have to delete the value to see the reflection
in another input box, or I have to change the value to see the reflection in another box (but it will be the previous value).
Same story with Select items, if I select Ethereum and Bitcoin, for example, it doesn’t work. If I change the second token
to DASH, it will show me the price of Ethereum Bitcoin (the previous pair). I’m doing something wrong in my code, I’ll be glad to get any advice
or solution to my problem
Here is the code:
import React, { useState } from 'react';
const Exchange = () => {
const [SelectOne, setSelectOne] = useState(null);
const [SelectTwo, setSelectTwo] = useState(null);
const [DataOne, setDataOne] = useState(null);
const [DataTwo, setDataTwo] = useState(null);
const crypto = [
'btc',
'eth',
'dash',
'dot',
'xmr',
'bnb',
'bch',
'etc',
'zec',
'sol',
'ltc',
'trx',
'zrx',
'xrp',
'usdt',
'usdt',
'xtz',
'matic',
'shib',
'doge'
];
function makeRequest(from, to, amount, which) {
let accessor = crypto[to].toUpperCase();
fetch(`https://min-api.cryptocompare.com/data/price?fsym=${crypto[from]}&tsyms=${crypto[to]}`)
.then(res => res.json())
.then(data => {
if (which) {
setDataTwo(data[accessor] * amount);
} else {
setDataOne(data[accessor] * amount);
}
});
}
function getSelectorTwo(val) {
setSelectTwo(val.target.value);
console.log(val.target.value);
if (SelectOne || SelectTwo != null && SelectOne != SelectTwo) {
makeRequest(SelectOne, SelectTwo, DataOne, true);
}
}
function getSelectorOne(val) {
setSelectOne(val.target.value);
console.log(val.target.value);
if (SelectOne || SelectTwo != null && SelectOne != SelectTwo) {
makeRequest(SelectOne, SelectTwo, DataOne, true);
}
}
function getValueOne(val) {
setDataOne(val.target.value);
console.log(val.target.value);
if (SelectOne || SelectTwo != null && SelectOne != SelectTwo) {
makeRequest(SelectOne, SelectTwo, DataOne, true);
}
}
function getValueTwo(val) {
setDataTwo(val.target.value);
console.log(val.target.value);
if (SelectOne || SelectTwo != null && SelectOne != SelectTwo) {
makeRequest(SelectTwo, SelectOne, DataTwo, false);
}
}
return (
<div className="bg-sky-500 h-screen w-screen">
<div className='container mx-auto '>
<div className='grid grid-cols-1 gap-4'>
<div>
<select className='rounded-full text-xl' onChange={getSelectorOne}>
<option value={27}>Select currency to send:</option>
<option value={0}>Bitcoin</option>
<option value={1}>Ethereum</option>
<option value={2}>DASH</option>
<option value={3}>Polkadot</option>
<option value={4}>Monero</option>
<option value={5}>Binance Smart Chain</option>
<option value={6}>Bitcoin Cash</option>
<option value={7}>Ethereum Classic</option>
<option value={8}>Zcash</option>
<option value={9}>Solana</option>
<option value={10}>Litecoin</option>
<option value={11}>Tron</option>
<option value={12}>0x</option>
<option value={13}>Ripple</option>
<option value={14}>Tether (ERC20)</option>
<option value={15}>Tether (TRC20)</option>
<option value={16}>Tezos</option>
<option value={17}>Polygon</option>
<option value={18}>Shiba Inu</option>
<option value={19}>Dogecoin</option>
</select>
</div>
<div>
<select className='rounded-full text-xl' onChange={getSelectorTwo}>
<option value={27}>Select currency to send:</option>
<option value={0}>Bitcoin</option>
<option value={1}>Ethereum</option>
<option value={2}>DASH</option>
<option value={3}>Polkadot</option>
<option value={4}>Monero</option>
<option value={5}>Binance Smart Chain</option>
<option value={6}>Bitcoin Cash</option>
<option value={7}>Ethereum Classic</option>
<option value={8}>Zcash</option>
<option value={9}>Solana</option>
<option value={10}>Litecoin</option>
<option value={11}>Tron</option>
<option value={12}>0x</option>
<option value={13}>Ripple</option>
<option value={14}>Tether (ERC20)</option>
<option value={15}>Tether (TRC20)</option>
<option value={16}>Tezos</option>
<option value={17}>Polygon</option>
<option value={18}>Shiba Inu</option>
<option value={19}>Dogecoin</option>
</select>
</div>
<div >
<input type={'number'} onChange={getValueOne} value={DataOne} placeholder={'Amout in'} className='p-6 rounded-lg bg-sky-600 outline-none'></input>
</div>
<div className=''>
<input type={'number'} onChange={getValueTwo} value={DataTwo} placeholder={'Amount out'} className='p-6 rounded-lg bg-sky-600 outline-none'></input>
</div>
</div>
</div>
</div>
)
}
I just don’t know where to start researching my problem. I couldn’t find the mistake while reading the React documentation. I’m stuck on it.
And I can’t continue my project without this feature.
2
Answers
You’re gonna slap yourself on the head for this one
Your functions were using the old values calling makeRequest(). You were getting val.target.value, which is the new value, but your original value was null, so it would get the value 14.49 and multiply it by 0, giving you a DataTwo of 0!
Setting react state is asynchronous.
If you create a state like this:
And then you call:
Then
SelectOne
will not have a new value until your component renders again, some milliseconds later.What you want here is an effect that calls
makeRequest
when one of the state values it depends on changes.And now
getSelectorOne()
(and its friends) is simply something like:Then when that is called:
SelectOne
in the dependency array has changed since the last renderIn summary, your functions that change state should not also directly trigger hidden side effects.
Instead, your state changes themselves should trigger side effects via
useEffect()
with a dependency array that includes the state values that the effect depends on.