React计算器总是迟到

huangapple go评论106阅读模式
英文:

React calculator is always late

问题

我的加密货币计算器应该根据API调用更改相反的字段(输入金额,输出金额)。例如,如果我选择以太坊 - 比特币对,并在以太坊字段中输入1,则比特币字段中将显示0.06902。如果我在比特币字段中输入1,则以太坊字段中将显示14.49(实际汇率)。我的计算器确实做到了这一点,但我不能立即看到变化。我必须删除值才能看到另一个输入框中的反映,或者我必须更改值才能看到另一个框中的反映(但这将是先前的值)。与选择项目相同的故事,如果我选择以太坊和比特币,例如,它不起作用。如果我将第二个令牌更改为DASH,它将显示以太坊比特币的价格(先前的对)。我在代码中做错了什么,我很乐意得到任何建议或解决我的问题的解决方案。

英文:

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.

答案1

得分: 0

你的函数在调用 makeRequest() 时使用了旧的数值。你获取了 val.target.value,这是新的数值,但你原来的数值是 null,因此它会获取到数值 14.49 并将其乘以 0,导致 DataTwo 变为 0!

英文:

You're gonna slap yourself on the head for this one

import { useState } from 'react';
import './App.css';

function App() {
	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 => {
			console.log('DATA', data[accessor] * amount);
            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 !== null || SelectTwo !== null) && SelectOne !== SelectTwo) {
            makeRequest(SelectOne, SelectTwo, DataOne, true);
        }
    }

    function getSelectorOne(val) {
        setSelectOne(val.target.value);
        if ((SelectOne !== null || SelectTwo !== null) && SelectOne !== SelectTwo) {
            makeRequest(SelectOne, SelectTwo, DataOne, true);
        }
    }

    // Here, you were using DataOne instead of the new value from your input
    function getValueOne(val) {
        setDataOne(val.target.value);
        console.log(val.target.value);
        if ((SelectOne !== null || SelectTwo !== null) && SelectOne !== SelectTwo) {
            makeRequest(SelectOne, SelectTwo, val.target.value, true);
        }
    }

    // Here, you were using DataTwo instead of the new value from your input
    function getValueTwo(val) {
        setDataTwo(val.target.value);
        if ((SelectOne !== null || SelectTwo != null) && SelectOne !== SelectTwo) {
            makeRequest(SelectTwo, SelectOne, val.target.value, 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>
    )
}

export default App;

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!

答案2

得分: 0

设置 React 的状态是异步的。

如果你像这样创建一个状态:

const [SelectOne, setSelectOne] = useState(null);

然后你调用:

setSelectOne(val.target.value);
console.log(val.target.value); // 新数值
console.log(SelectOne); // 旧数值

那么 SelectOne 在组件再次渲染几毫秒后才会有新的数值。


这里你需要一个效果(effect),当状态值改变时调用 makeRequest

useEffect(() => {
  if (SelectOne || SelectTwo != null && SelectOne != SelectTwo) {
    makeRequest(SelectOne, SelectTwo, DataOne, true);
    // 但你需要弄清楚最后这个布尔参数
  }
}, [SelectOne, SelectTwo, DataOne, DataTwo])

现在 getSelectorOne()(以及类似的函数)只是这样的:

function getSelectorOne(val) {
  setSelectOne(val.target.value);
}

然后当它被调用时:

  1. 组件由于状态改变而重新渲染。
  2. 效果注意到依赖数组中的 SelectOne 自上次渲染以来已经改变。
  3. 函数被执行,发起你的请求。

总之,改变状态的函数不应直接触发隐藏的副作用。

相反,状态的改变本身应该通过 useEffect() 触发依赖数组中包含的状态值的副作用。

英文:

Setting react state is asynchronous.

If you create a state like this:

const [SelectOne, setSelectOne] = useState(null);

And then you call:

setSelectOne(val.target.value);
console.log(val.target.value); // new value
console.log(SelectOne); // old value

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.

useEffect(() => {
if (SelectOne || SelectTwo != null && SelectOne != SelectTwo) {
makeRequest(SelectOne, SelectTwo, DataOne, true);
// You will have to figure out this last boolean argument, though
}
}, [SelectOne, SelectTwo, DataOne, DataTwo])

And now getSelectorOne() (and its friends) is simply something like:

function getSelectorOne(val) {
setSelectOne(val.target.value);
}

Then when that is called:

  1. The component re-renders due to the state change.
  2. The effect notices that SelectOne in the dependency array has changed since the last render
  3. The function is executed, making your request

In 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.

huangapple
  • 本文由 发表于 2023年2月16日 06:52:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/75466172.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定