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
相关产品推荐
相关产品推荐

