React加密货币计算器实时同步异常问题求助
修复方案:React加密货币计算器双向同步与币种切换问题
核心问题分析
- useState异步更新延迟:调用
setDataOne/setSelectOne后立刻执行makeRequest,此时状态尚未完成更新,使用旧值计算导致同步结果滞后。 - 币种切换未清理旧数据:切换币种对时未清空原有金额,也未基于新币种对发起请求,导致显示旧汇率数据。
- 条件判断逻辑错误:原判断语句
if (SelectOne || SelectTwo != null && SelectOne != SelectTwo)优先级混乱,未明确确保两个币种都已选择且不同。
修复后的完整代码
import React, { useState, useEffect } from 'react'; const Exchange = () => { const [selectOne, setSelectOne] = useState(null); const [selectTwo, setSelectTwo] = useState(null); const [dataOne, setDataOne] = useState(''); const [dataTwo, setDataTwo] = useState(''); const crypto = [ 'btc', 'eth', 'dash', 'dot', 'xmr', 'bnb', 'bch', 'etc', 'zec', 'sol', 'ltc', 'trx', 'zrx', 'xrp', 'usdt', 'usdt', 'xtz', 'matic', 'shib', 'doge' ]; // 统一汇率计算函数 const calculateExchange = (from, to, amount, targetSetter) => { if (!from || !to || from === to || !amount || isNaN(amount)) return; const fromSymbol = crypto[from]; const toSymbol = crypto[to]; fetch(`https://min-api.cryptocompare.com/data/price?fsym=${fromSymbol}&tsyms=${toSymbol}`) .then(res => res.json()) .then(data => { const rate = data[toSymbol.toUpperCase()]; targetSetter((rate * parseFloat(amount)).toFixed(5)); // 匹配示例精度 }) .catch(err => console.error('汇率请求失败:', err)); }; // 监听币种切换,重置金额并触发计算 useEffect(() => { if (selectOne && selectTwo && selectOne !== selectTwo) { setDataOne(''); setDataTwo(''); if (dataOne) calculateExchange(selectOne, selectTwo, dataOne, setDataTwo); } }, [selectOne, selectTwo]); // 监听第一个输入框变化,同步更新第二个 useEffect(() => { if (selectOne && selectTwo && selectOne !== selectTwo && dataOne) { calculateExchange(selectOne, selectTwo, dataOne, setDataTwo); } }, [dataOne, selectOne, selectTwo]); // 监听第二个输入框变化,同步更新第一个 useEffect(() => { if (selectOne && selectTwo && selectOne !== selectTwo && dataTwo) { calculateExchange(selectTwo, selectOne, dataTwo, setDataOne); } }, [dataTwo, selectOne, selectTwo]); 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={(e) => setSelectOne(e.target.value)} value={selectOne ?? 27} > <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={(e) => setSelectTwo(e.target.value)} value={selectTwo ?? 27} > <option value={27}>Select currency to receive:</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={(e) => setDataOne(e.target.value)} value={dataOne} placeholder={'Amount in'} className='p-6 rounded-lg bg-sky-600 outline-none' /> </div> <div className=''> <input type={'number'} onChange={(e) => setDataTwo(e.target.value)} value={dataTwo} placeholder={'Amount out'} className='p-6 rounded-lg bg-sky-600 outline-none' /> </div> </div> </div> </div> ) }
关键修改说明
- 用useEffect监听状态变化:确保状态更新完成后再触发汇率计算,彻底解决异步更新导致的旧值问题。
- 币种切换重置金额:切换币种对时自动清空输入框,避免旧数据残留,同时保留自动计算逻辑。
- 统一计算逻辑:抽离汇率计算函数,减少重复代码,增加边界判断(如币种相同、金额为空时不请求)。
- 受控select组件:给select绑定明确的value值,避免选中状态异常。
- 精度控制:用
toFixed(5)保留小数位数,匹配示例中的精度要求。
内容的提问来源于stack exchange,提问作者JeffReactorWell
相关产品推荐
相关产品推荐

