You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React加密货币计算器实时同步异常问题求助

修复方案:React加密货币计算器双向同步与币种切换问题

核心问题分析

  1. useState异步更新延迟:调用setDataOne/setSelectOne后立刻执行makeRequest,此时状态尚未完成更新,使用旧值计算导致同步结果滞后。
  2. 币种切换未清理旧数据:切换币种对时未清空原有金额,也未基于新币种对发起请求,导致显示旧汇率数据。
  3. 条件判断逻辑错误:原判断语句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>
    )
}

关键修改说明

  1. 用useEffect监听状态变化:确保状态更新完成后再触发汇率计算,彻底解决异步更新导致的旧值问题。
  2. 币种切换重置金额:切换币种对时自动清空输入框,避免旧数据残留,同时保留自动计算逻辑。
  3. 统一计算逻辑:抽离汇率计算函数,减少重复代码,增加边界判断(如币种相同、金额为空时不请求)。
  4. 受控select组件:给select绑定明确的value值,避免选中状态异常。
  5. 精度控制:用toFixed(5)保留小数位数,匹配示例中的精度要求。

内容的提问来源于stack exchange,提问作者JeffReactorWell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 23:25:27