-
Notifications
You must be signed in to change notification settings - Fork 2.2k
fix(web): remove circular dependency causing trading symbols input bug (#632) #671
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(web): remove circular dependency causing trading symbols input bug (#632) #671
Conversation
NoFxAiOS#632) **Problem:** Unable to type comma-separated trading symbols in the input field. When typing "BTCUSDT," → comma immediately disappears → cannot add more symbols. **Root Cause:** Circular state dependency between `useEffect` and `handleInputChange`: ```typescript // ❌ Lines 146-149: useEffect syncs selectedCoins → formData useEffect(() => { const symbolsString = selectedCoins.join(',') setFormData(prev => ({ ...prev, trading_symbols: symbolsString })) }, [selectedCoins]) // Lines 150-153: handleInputChange syncs formData → selectedCoins if (field === 'trading_symbols') { const coins = value.split(',').map(...).filter(...) setSelectedCoins(coins) } ``` **Execution Flow:** 1. User types: `"BTCUSDT,"` 2. `handleInputChange` fires → splits by comma → filters empty → `selectedCoins = ["BTCUSDT"]` 3. `useEffect` fires → joins → overwrites input to `"BTCUSDT"` ❌ **Trailing comma removed!** 4. User cannot continue typing **Solution:** Remove the redundant `useEffect` (lines 146-149) and update `handleCoinToggle` to directly sync both states: ```typescript // ✅ handleCoinToggle now updates both states const handleCoinToggle = (coin: string) => { setSelectedCoins(prev => { const newCoins = prev.includes(coin) ? ... : ... // Directly update formData.trading_symbols const symbolsString = newCoins.join(',') setFormData(current => ({ ...current, trading_symbols: symbolsString })) return newCoins }) } ``` **Why This Works:** - **Quick selector buttons** (`handleCoinToggle`): Now updates both states ✅ - **Manual input** (`handleInputChange`): Already updates both states ✅ - **No useEffect interference**: User can type freely ✅ **Impact:** - ✅ Manual typing of comma-separated symbols now works - ✅ Quick selector buttons still work correctly - ✅ No circular dependency - ✅ Cleaner unidirectional data flow Fixes NoFxAiOS#632 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
🤖 Advisory Check ResultsThese are advisory checks to help improve code quality. They won't block your PR from being merged. 📋 PR InformationTitle Format: ✅ Good - Follows Conventional Commits 🔧 Backend ChecksGo Formatting: Files needing formattingGo Vet: ✅ Good Fix locally: go fmt ./... # Format code
go vet ./... # Check for issues
go test ./... # Run tests⚛️ Frontend ChecksBuild & Type Check: ✅ Success Fix locally: cd web
npm run build # Test build (includes type checking)📖 ResourcesQuestions? Feel free to ask in the comments! 🙏 These checks are advisory and won't block your PR from being merged. This comment is automatically generated from pr-checks-run.yml. |
代码审查报告 - PR #671审查结果:✅ 通过(重要bug修复)业务层面审查✅ 需求验证
✅ 功能完整性
技术层面审查✅ 问题分析循环依赖问题: // Before - 存在循环依赖
useEffect(() => {
const symbolsString = selectedCoins.join(',')
setFormData((prev) => ({ ...prev, trading_symbols: symbolsString }))
}, [selectedCoins])
// 问题流程:
// 1. handleCoinToggle 更新 selectedCoins
// 2. useEffect 触发,更新 formData.trading_symbols
// 3. setFormData 可能触发其他 useEffect
// 4. 可能导致无限循环或状态不一致症状:
✅ 解决方案移除useEffect,直接在事件处理器中更新: const handleCoinToggle = (coin: string) => {
setSelectedCoins((prev) => {
// 1. 计算新的 coins 数组
const newCoins = prev.includes(coin)
? prev.filter((c) => c !== coin)
: [...prev, coin]
// 2. 同时更新 formData.trading_symbols
const symbolsString = newCoins.join(',')
setFormData((current) => ({ ...current, trading_symbols: symbolsString }))
// 3. 返回新的 coins
return newCoins
})
}优点:
✅ 代码改进Before: // 两次setState调用,通过useEffect连接
setSelectedCoins(newCoins) // → 触发useEffect
// → setFormData()After: // 一次setState回调中完成所有更新
setSelectedCoins((prev) => {
const newCoins = ...
setFormData(...) // 同步更新
return newCoins
})E2E 验证报告✅ 测试场景1:选中币种✅ 测试场景2:取消选中币种✅ 测试场景3:多次快速点击✅ 测试场景4:手动编辑输入框React最佳实践验证✅ 状态管理原则:避免通过useEffect同步状态 错误模式: // ❌ 不要这样做
const [stateA, setStateA] = useState()
const [stateB, setStateB] = useState()
useEffect(() => {
setStateB(deriveFromStateA(stateA))
}, [stateA])正确模式: // ✅ 应该这样做
const handleChange = () => {
const newA = ...
setStateA(newA)
setStateB(deriveFromStateA(newA))
}
// 或者使用派生状态
const stateB = useMemo(() => deriveFromStateA(stateA), [stateA])本PR采用:✅ 正确模式(在事件处理器中同步更新) ✅ 性能优化减少渲染次数:
渲染次数:从2次减少到1次 代码质量审查✅ 代码简洁性
✅ 可维护性
✅ 类型安全
🟡 改进建议(非 BLOCKING)建议1:添加注释const handleCoinToggle = (coin: string) => {
setSelectedCoins((prev) => {
// Calculate new coins array
const newCoins = prev.includes(coin)
? prev.filter((c) => c !== coin)
: [...prev, coin]
// Sync with formData to keep both states consistent
const symbolsString = newCoins.join(',')
setFormData((current) => ({ ...current, trading_symbols: symbolsString }))
return newCoins
})
}建议2:考虑使用useReducer// 如果状态逻辑变得复杂,可以考虑 useReducer
const [state, dispatch] = useReducer(traderConfigReducer, initialState)
const handleCoinToggle = (coin: string) => {
dispatch({ type: 'TOGGLE_COIN', payload: coin })
}
// reducer 中统一处理状态更新
function traderConfigReducer(state, action) {
switch (action.type) {
case 'TOGGLE_COIN':
const newCoins = state.selectedCoins.includes(action.payload)
? state.selectedCoins.filter(c => c !== action.payload)
: [...state.selectedCoins, action.payload]
return {
...state,
selectedCoins: newCoins,
formData: {
...state.formData,
trading_symbols: newCoins.join(',')
}
}
}
}建议3:提取常量const SYMBOL_SEPARATOR = ','
const symbolsString = newCoins.join(SYMBOL_SEPARATOR)建议4:添加单元测试describe('handleCoinToggle', () => {
it('adds coin when not selected', () => {
const { result } = renderHook(() => useTraderConfig())
act(() => {
result.current.handleCoinToggle('BTCUSDT')
})
expect(result.current.selectedCoins).toContain('BTCUSDT')
expect(result.current.formData.trading_symbols).toBe('BTCUSDT')
})
it('removes coin when already selected', () => {
const { result } = renderHook(() => useTraderConfig())
act(() => {
result.current.handleCoinToggle('BTCUSDT')
result.current.handleCoinToggle('BTCUSDT')
})
expect(result.current.selectedCoins).not.toContain('BTCUSDT')
expect(result.current.formData.trading_symbols).toBe('')
})
})常见问题验证✅ 避免的常见陷阱陷阱1:useState在循环中调用
陷阱2:useEffect依赖数组遗漏
陷阱3:状态更新不是原子操作
陷阱4:直接修改state
总结这是一个重要的bug修复 PR: 优点
改进空间(非 BLOCKING)
审查结论:✅ 通过,强烈建议合并 重要性:
影响范围:
|
NoFxAiOS#632) (NoFxAiOS#671) **Problem:** Unable to type comma-separated trading symbols in the input field. When typing "BTCUSDT," → comma immediately disappears → cannot add more symbols. **Root Cause:** Circular state dependency between `useEffect` and `handleInputChange`: ```typescript // ❌ Lines 146-149: useEffect syncs selectedCoins → formData useEffect(() => { const symbolsString = selectedCoins.join(',') setFormData(prev => ({ ...prev, trading_symbols: symbolsString })) }, [selectedCoins]) // Lines 150-153: handleInputChange syncs formData → selectedCoins if (field === 'trading_symbols') { const coins = value.split(',').map(...).filter(...) setSelectedCoins(coins) } ``` **Execution Flow:** 1. User types: `"BTCUSDT,"` 2. `handleInputChange` fires → splits by comma → filters empty → `selectedCoins = ["BTCUSDT"]` 3. `useEffect` fires → joins → overwrites input to `"BTCUSDT"` ❌ **Trailing comma removed!** 4. User cannot continue typing **Solution:** Remove the redundant `useEffect` (lines 146-149) and update `handleCoinToggle` to directly sync both states: ```typescript // ✅ handleCoinToggle now updates both states const handleCoinToggle = (coin: string) => { setSelectedCoins(prev => { const newCoins = prev.includes(coin) ? ... : ... // Directly update formData.trading_symbols const symbolsString = newCoins.join(',') setFormData(current => ({ ...current, trading_symbols: symbolsString })) return newCoins }) } ``` **Why This Works:** - **Quick selector buttons** (`handleCoinToggle`): Now updates both states ✅ - **Manual input** (`handleInputChange`): Already updates both states ✅ - **No useEffect interference**: User can type freely ✅ **Impact:** - ✅ Manual typing of comma-separated symbols now works - ✅ Quick selector buttons still work correctly - ✅ No circular dependency - ✅ Cleaner unidirectional data flow Fixes NoFxAiOS#632 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <[email protected]> (cherry picked from commit 1502b7b)
NoFxAiOS#632) (NoFxAiOS#671) **Problem:** Unable to type comma-separated trading symbols in the input field. When typing "BTCUSDT," → comma immediately disappears → cannot add more symbols. **Root Cause:** Circular state dependency between `useEffect` and `handleInputChange`: ```typescript // ❌ Lines 146-149: useEffect syncs selectedCoins → formData useEffect(() => { const symbolsString = selectedCoins.join(',') setFormData(prev => ({ ...prev, trading_symbols: symbolsString })) }, [selectedCoins]) // Lines 150-153: handleInputChange syncs formData → selectedCoins if (field === 'trading_symbols') { const coins = value.split(',').map(...).filter(...) setSelectedCoins(coins) } ``` **Execution Flow:** 1. User types: `"BTCUSDT,"` 2. `handleInputChange` fires → splits by comma → filters empty → `selectedCoins = ["BTCUSDT"]` 3. `useEffect` fires → joins → overwrites input to `"BTCUSDT"` ❌ **Trailing comma removed!** 4. User cannot continue typing **Solution:** Remove the redundant `useEffect` (lines 146-149) and update `handleCoinToggle` to directly sync both states: ```typescript // ✅ handleCoinToggle now updates both states const handleCoinToggle = (coin: string) => { setSelectedCoins(prev => { const newCoins = prev.includes(coin) ? ... : ... // Directly update formData.trading_symbols const symbolsString = newCoins.join(',') setFormData(current => ({ ...current, trading_symbols: symbolsString })) return newCoins }) } ``` **Why This Works:** - **Quick selector buttons** (`handleCoinToggle`): Now updates both states ✅ - **Manual input** (`handleInputChange`): Already updates both states ✅ - **No useEffect interference**: User can type freely ✅ **Impact:** - ✅ Manual typing of comma-separated symbols now works - ✅ Quick selector buttons still work correctly - ✅ No circular dependency - ✅ Cleaner unidirectional data flow Fixes NoFxAiOS#632 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <[email protected]>
NoFxAiOS#632) (NoFxAiOS#671) **Problem:** Unable to type comma-separated trading symbols in the input field. When typing "BTCUSDT," → comma immediately disappears → cannot add more symbols. **Root Cause:** Circular state dependency between `useEffect` and `handleInputChange`: ```typescript // ❌ Lines 146-149: useEffect syncs selectedCoins → formData useEffect(() => { const symbolsString = selectedCoins.join(',') setFormData(prev => ({ ...prev, trading_symbols: symbolsString })) }, [selectedCoins]) // Lines 150-153: handleInputChange syncs formData → selectedCoins if (field === 'trading_symbols') { const coins = value.split(',').map(...).filter(...) setSelectedCoins(coins) } ``` **Execution Flow:** 1. User types: `"BTCUSDT,"` 2. `handleInputChange` fires → splits by comma → filters empty → `selectedCoins = ["BTCUSDT"]` 3. `useEffect` fires → joins → overwrites input to `"BTCUSDT"` ❌ **Trailing comma removed!** 4. User cannot continue typing **Solution:** Remove the redundant `useEffect` (lines 146-149) and update `handleCoinToggle` to directly sync both states: ```typescript // ✅ handleCoinToggle now updates both states const handleCoinToggle = (coin: string) => { setSelectedCoins(prev => { const newCoins = prev.includes(coin) ? ... : ... // Directly update formData.trading_symbols const symbolsString = newCoins.join(',') setFormData(current => ({ ...current, trading_symbols: symbolsString })) return newCoins }) } ``` **Why This Works:** - **Quick selector buttons** (`handleCoinToggle`): Now updates both states ✅ - **Manual input** (`handleInputChange`): Already updates both states ✅ - **No useEffect interference**: User can type freely ✅ **Impact:** - ✅ Manual typing of comma-separated symbols now works - ✅ Quick selector buttons still work correctly - ✅ No circular dependency - ✅ Cleaner unidirectional data flow Fixes NoFxAiOS#632 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <[email protected]>
#632) (#671) **Problem:** Unable to type comma-separated trading symbols in the input field. When typing "BTCUSDT," → comma immediately disappears → cannot add more symbols. **Root Cause:** Circular state dependency between `useEffect` and `handleInputChange`: ```typescript // ❌ Lines 146-149: useEffect syncs selectedCoins → formData useEffect(() => { const symbolsString = selectedCoins.join(',') setFormData(prev => ({ ...prev, trading_symbols: symbolsString })) }, [selectedCoins]) // Lines 150-153: handleInputChange syncs formData → selectedCoins if (field === 'trading_symbols') { const coins = value.split(',').map(...).filter(...) setSelectedCoins(coins) } ``` **Execution Flow:** 1. User types: `"BTCUSDT,"` 2. `handleInputChange` fires → splits by comma → filters empty → `selectedCoins = ["BTCUSDT"]` 3. `useEffect` fires → joins → overwrites input to `"BTCUSDT"` ❌ **Trailing comma removed!** 4. User cannot continue typing **Solution:** Remove the redundant `useEffect` (lines 146-149) and update `handleCoinToggle` to directly sync both states: ```typescript // ✅ handleCoinToggle now updates both states const handleCoinToggle = (coin: string) => { setSelectedCoins(prev => { const newCoins = prev.includes(coin) ? ... : ... // Directly update formData.trading_symbols const symbolsString = newCoins.join(',') setFormData(current => ({ ...current, trading_symbols: symbolsString })) return newCoins }) } ``` **Why This Works:** - **Quick selector buttons** (`handleCoinToggle`): Now updates both states ✅ - **Manual input** (`handleInputChange`): Already updates both states ✅ - **No useEffect interference**: User can type freely ✅ **Impact:** - ✅ Manual typing of comma-separated symbols now works - ✅ Quick selector buttons still work correctly - ✅ No circular dependency - ✅ Cleaner unidirectional data flow Fixes #632 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: tinkle-community <[email protected]>
#632) (#671) **Problem:** Unable to type comma-separated trading symbols in the input field. When typing "BTCUSDT," → comma immediately disappears → cannot add more symbols. **Root Cause:** Circular state dependency between `useEffect` and `handleInputChange`: ```typescript // ❌ Lines 146-149: useEffect syncs selectedCoins → formData useEffect(() => { const symbolsString = selectedCoins.join(',') setFormData(prev => ({ ...prev, trading_symbols: symbolsString })) }, [selectedCoins]) // Lines 150-153: handleInputChange syncs formData → selectedCoins if (field === 'trading_symbols') { const coins = value.split(',').map(...).filter(...) setSelectedCoins(coins) } ``` **Execution Flow:** 1. User types: `"BTCUSDT,"` 2. `handleInputChange` fires → splits by comma → filters empty → `selectedCoins = ["BTCUSDT"]` 3. `useEffect` fires → joins → overwrites input to `"BTCUSDT"` ❌ **Trailing comma removed!** 4. User cannot continue typing **Solution:** Remove the redundant `useEffect` (lines 146-149) and update `handleCoinToggle` to directly sync both states: ```typescript // ✅ handleCoinToggle now updates both states const handleCoinToggle = (coin: string) => { setSelectedCoins(prev => { const newCoins = prev.includes(coin) ? ... : ... // Directly update formData.trading_symbols const symbolsString = newCoins.join(',') setFormData(current => ({ ...current, trading_symbols: symbolsString })) return newCoins }) } ``` **Why This Works:** - **Quick selector buttons** (`handleCoinToggle`): Now updates both states ✅ - **Manual input** (`handleInputChange`): Already updates both states ✅ - **No useEffect interference**: User can type freely ✅ **Impact:** - ✅ Manual typing of comma-separated symbols now works - ✅ Quick selector buttons still work correctly - ✅ No circular dependency - ✅ Cleaner unidirectional data flow Fixes #632 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: tinkle-community <[email protected]>
Pull Request - Frontend
Summary
Fixes inability to manually type comma-separated trading symbols in the trader configuration modal. Users could not type commas to separate multiple trading symbols - the comma would immediately disappear.
Problem
User Experience:
Quick selector buttons work fine, but manual typing is broken.
Root Cause
File:
web/src/components/TraderConfigModal.tsxCircular state dependency between
useEffect(lines 156-160) andhandleInputChange(lines 167-174):Execution Flow (The Bug):
"BTCUSDT,"handleInputChangefires["BTCUSDT", ""]["BTCUSDT"]❌selectedCoins = ["BTCUSDT"]useEffectfires (triggered by selectedCoins change)"BTCUSDT"Solution
Remove the redundant
useEffectand makehandleCoinToggledirectly update both states:Why This Works:
handleCoinToggle): Updates bothselectedCoinsANDformData.trading_symbols✅handleInputChange): Already updates both states ✅Unidirectional Data Flow:
Changes
File:
web/src/components/TraderConfigModal.tsxuseEffect(lines 156-160)handleCoinToggleto sync both states directlyImpact
Testing
Fixes #632
Co-Authored-By: Claude [email protected]
🎯 Type of Change | 變更類型
🔗 Related Issues | 相關 Issue
🧪 Testing | 測試
Test Environment | 測試環境
Manual Testing | 手動測試
🌐 Internationalization | 國際化
✅ Checklist | 檢查清單
Code Quality | 代碼質量
npm run build)npm run lint| 已運行npm run lintDocumentation | 文檔
Git
devbranch | 已 rebase 到最新dev分支By submitting this PR, I confirm | 提交此 PR,我確認:
🌟 Thank you for your contribution! | 感謝你的貢獻!