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

React货币转换器:避免递归动态更新两个输入框状态

解决方案:避免无限循环的双向货币转换实现

核心思路是仅在用户主动输入时触发汇率转换,自动更新的输入框值不会触发反向转换,从根源上杜绝无限循环。

步骤1:重构输入框 onChange 处理逻辑

将转换逻辑绑定到用户输入事件,而非监听 state 变化。通过输入框 id 区分操作源,只在用户手动输入时触发对应方向的转换。

import { useState, useRef, useEffect } from 'react';
import axios from 'axios';

function CurrencyConverter() {
  const [state1, setState1] = useState({
    value: '',
    currency: 'USD',
    src: 'https://hatscripts.github.io/circle-flags/flags/us.svg',
  });
  const [state2, setState2] = useState({
    value: '',
    currency: 'INR',
    src: 'https://hatscripts.github.io/circle-flags/flags/in.svg',
  });
  const [loading, setLoading] = useState(false);
  const timeoutRef = useRef(null); // 用于防抖定时器

  // 处理用户输入事件
  const handleChange = (e) => {
    const { id, value } = e.target;
    
    // 清除之前的防抖定时器,避免频繁调用API
    if (timeoutRef.current) clearTimeout(timeoutRef.current);

    timeoutRef.current = setTimeout(() => {
      if (id === '1') {
        // 用户手动修改输入框1,更新state1并触发转换到输入框2
        setState1(prev => ({ ...prev, value }));
        if (value) {
          fetchRateFrom1To2(value);
        } else {
          setState2(prev => ({ ...prev, value: '' }));
        }
      } else if (id === '2') {
        // 用户手动修改输入框2,更新state2并触发转换到输入框1
        setState2(prev => ({ ...prev, value }));
        if (value) {
          fetchRateFrom2To1(value);
        } else {
          setState1(prev => ({ ...prev, value: '' }));
        }
      }
    }, 2000);
  };

  // 从输入框1的货币转换到输入框2的货币
  async function fetchRateFrom1To2(inputValue) {
    setLoading(true);
    try {
      const response = await axios.get('http://localhost:4000/convert', {
        params: {
          from: state1.currency,
          to: state2.currency,
        },
      });
      setState2(prev => ({
        ...prev,
        value: (inputValue * response.data.value).toFixed(4),
      }));
    } catch (error) {
      console.error('转换失败:', error);
    }
    setLoading(false);
  }

  // 从输入框2的货币转换到输入框1的货币
  async function fetchRateFrom2To1(inputValue) {
    setLoading(true);
    try {
      const response = await axios.get('http://localhost:4000/convert', {
        params: {
          from: state2.currency,
          to: state1.currency,
        },
      });
      setState1(prev => ({
        ...prev,
        value: (inputValue * response.data.value).toFixed(4),
      }));
    } catch (error) {
      console.error('转换失败:', error);
    }
    setLoading(false);
  }

  // 组件卸载时清除定时器
  useEffect(() => {
    return () => {
      if (timeoutRef.current) clearTimeout(timeoutRef.current);
    };
  }, []);

  return (
    <>
      <input
        id="1"
        placeholder="0"
        type="number"
        value={state1.value}
        onChange={handleChange}
        disabled={loading}
      />
      {/* 此处省略货币选择器等其他组件 */}
      <input
        id="2"
        placeholder="0"
        type="number"
        value={state2.value}
        onChange={handleChange}
        disabled={loading}
      />
    </>
  );
}

export default CurrencyConverter;

为什么能避免无限循环?

  • 只有用户手动触发 onChange 时才会执行转换逻辑,自动更新的输入框值(比如通过 setState2 更新的输入框2内容)不会触发 onChange 事件,也就不会反向触发输入框1的转换。
  • 防抖逻辑通过 useRef 保存定时器ID,避免用户连续输入时频繁调用API,同时不影响正常的转换流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:07:01