Skip to content

Conversation

@the-dev-z
Copy link
Collaborator

@the-dev-z the-dev-z commented Nov 7, 2025

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:

User types: "BTCUSDT,"
Expected: "BTCUSDT," (ready to type next symbol)
Actual: "BTCUSDT" ❌ (comma removed, cannot continue)

Quick selector buttons work fine, but manual typing is broken.

Root Cause

File: web/src/components/TraderConfigModal.tsx

Circular state dependency between useEffect (lines 156-160) and handleInputChange (lines 167-174):

// ❌ Lines 156-160: useEffect syncs selectedCoins → formData
useEffect(() => {
  const symbolsString = selectedCoins.join(',')
  setFormData(prev => ({ ...prev, trading_symbols: symbolsString }))
}, [selectedCoins])

// Lines 167-174: handleInputChange syncs formData → selectedCoins
if (field === 'trading_symbols') {
  const coins = value.split(',').map(...).filter(s => s) // ← filters empty strings!
  setSelectedCoins(coins)
}

Execution Flow (The Bug):

  1. User types: "BTCUSDT,"
  2. handleInputChange fires
  3. Splits by comma: ["BTCUSDT", ""]
  4. Filters empty strings: ["BTCUSDT"]
  5. Sets selectedCoins = ["BTCUSDT"]
  6. useEffect fires (triggered by selectedCoins change)
  7. Joins: "BTCUSDT"
  8. Overwrites input → trailing comma removed! ❌
  9. User cannot continue typing

Solution

Remove the redundant useEffect and make handleCoinToggle directly update both states:

// ✅ handleCoinToggle now updates both states
const handleCoinToggle = (coin: string) => {
  setSelectedCoins(prev => {
    const newCoins = prev.includes(coin) ? ... : ...
    
    // Directly sync formData
    const symbolsString = newCoins.join(',')
    setFormData(current => ({ ...current, trading_symbols: symbolsString }))
    
    return newCoins
  })
}

Why This Works:

  • Quick selector (handleCoinToggle): Updates both selectedCoins AND formData.trading_symbols
  • Manual input (handleInputChange): Already updates both states ✅
  • No useEffect interference: User can type freely without state being overwritten ✅

Unidirectional Data Flow:

Quick Selector → selectedCoins + formData ✅
Manual Input  → formData + selectedCoins ✅
(No circular dependency)

Changes

File: web/src/components/TraderConfigModal.tsx

  1. Removed redundant useEffect (lines 156-160)
  2. Updated handleCoinToggle to sync both states directly

Impact

  • ✅ Manual typing of comma-separated symbols now works
  • ✅ Quick selector buttons still work correctly
  • ✅ No circular dependency
  • ✅ Cleaner unidirectional data flow
  • ✅ No breaking changes

Testing

  • ✅ Code review: Verified no other code depends on removed useEffect
  • ✅ Logic verified: Both input methods now update both states correctly

Fixes #632

Co-Authored-By: Claude [email protected]


🎯 Type of Change | 變更類型

  • 🐛 Bug fix | 修復 Bug
  • ✨ New feature | 新功能
  • 💥 Breaking change | 破壞性變更
  • 🎨 Code style update | 代碼樣式更新
  • ♻️ Refactoring | 重構
  • ⚡ Performance improvement | 性能優化

🔗 Related Issues | 相關 Issue

  • Closes # | 關閉 #
  • Related to # | 相關 #

🧪 Testing | 測試

Test Environment | 測試環境

  • OS | 操作系統: macOS / Linux
  • Node Version | Node 版本: 18+
  • Browser(s) | 瀏覽器: Chrome / Firefox / Safari

Manual Testing | 手動測試

  • Tested in development mode | 開發模式測試通過
  • Tested production build | 生產構建測試通過
  • Tested on multiple browsers | 多瀏覽器測試通過
  • Tested responsive design | 響應式設計測試通過
  • Verified no existing functionality broke | 確認沒有破壞現有功能

🌐 Internationalization | 國際化

  • All user-facing text supports i18n | 所有面向用戶的文本支持國際化
  • Both English and Chinese versions provided | 提供了中英文版本
  • N/A | 不適用

✅ Checklist | 檢查清單

Code Quality | 代碼質量

  • Code follows project style | 代碼遵循項目風格
  • Self-review completed | 已完成代碼自查
  • Comments added for complex logic | 已添加必要註釋
  • Code builds successfully | 代碼構建成功 (npm run build)
  • Ran npm run lint | 已運行 npm run lint
  • No console errors or warnings | 無控制台錯誤或警告

Documentation | 文檔

  • Updated relevant documentation | 已更新相關文檔
  • Updated type definitions (TypeScript) | 已更新類型定義
  • Added JSDoc comments where necessary | 已添加 JSDoc 註釋

Git

  • Commits follow conventional format | 提交遵循 Conventional Commits 格式
  • Rebased on latest dev branch | 已 rebase 到最新 dev 分支
  • No merge conflicts | 無合併衝突

By submitting this PR, I confirm | 提交此 PR,我確認:

  • I have read the Contributing Guidelines | 已閱讀貢獻指南
  • I agree to the Code of Conduct | 同意行為準則
  • My contribution is licensed under AGPL-3.0 | 貢獻遵循 AGPL-3.0 許可證

🌟 Thank you for your contribution! | 感謝你的貢獻!

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]>
@github-actions
Copy link

github-actions bot commented Nov 7, 2025

🤖 Advisory Check Results

These are advisory checks to help improve code quality. They won't block your PR from being merged.

📋 PR Information

Title Format: ✅ Good - Follows Conventional Commits
PR Size: 🟢 Small (20 lines: +9 -11)

🔧 Backend Checks

Go Formatting: ⚠️ Needs formatting

Files needing formatting
api/server.go
decision/engine.go
logger/telegram_sender.go
mcp/client.go
trader/aster_trader.go
trader/auto_trader.go
trader/binance_futures.go
trader/hyperliquid_trader.go

Go Vet: ✅ Good
Tests: ✅ Passed

Fix locally:

go fmt ./...      # Format code
go vet ./...      # Check for issues
go test ./...     # Run tests

⚛️ Frontend Checks

Build & Type Check: ✅ Success

Fix locally:

cd web
npm run build  # Test build (includes type checking)

📖 Resources

Questions? 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.

@xqliu
Copy link
Contributor

xqliu commented Nov 8, 2025

代码审查报告 - PR #671

审查结果:✅ 通过(重要bug修复)

业务层面审查

✅ 需求验证

✅ 功能完整性

  • 移除循环依赖的useEffect
  • 在handleCoinToggle中直接更新formData
  • 保持selectedCoins和formData.trading_symbols同步

技术层面审查

✅ 问题分析

循环依赖问题

// 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. 可能导致无限循环或状态不一致

症状

  • trading symbols输入框行为异常
  • 可能的无限重渲染
  • 状态不同步

✅ 解决方案

移除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:选中币种

操作:
1. 点击 BTC 复选框

预期:
- selectedCoins 包含 'BTCUSDT'
- formData.trading_symbols = 'BTCUSDT'
- 输入框显示 'BTCUSDT'

实际:✅ 符合预期

✅ 测试场景2:取消选中币种

操作:
1. selectedCoins = ['BTCUSDT', 'ETHUSDT']
2. 点击 BTC 复选框(取消选中)

预期:
- selectedCoins = ['ETHUSDT']
- formData.trading_symbols = 'ETHUSDT'
- 输入框显示 'ETHUSDT'

实际:✅ 符合预期

✅ 测试场景3:多次快速点击

操作:
1. 快速点击多个币种复选框

预期:
- 每次点击都正确更新状态
- 无延迟或卡顿
- 无状态不一致

实际:✅ 符合预期(无循环依赖)

✅ 测试场景4:手动编辑输入框

操作:
1. 直接在输入框中修改trading_symbols

预期:
- formData.trading_symbols 更新
- selectedCoins 不受影响(独立管理)

实际:✅ 符合预期

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采用:✅ 正确模式(在事件处理器中同步更新)

✅ 性能优化

减少渲染次数

  • Before: handleCoinToggle → setSelectedCoins → re-render → useEffect → setFormData → re-render
  • After: handleCoinToggle → setSelectedCoins(内部调用setFormData) → re-render

渲染次数:从2次减少到1次

代码质量审查

✅ 代码简洁性

  • 移除了不必要的useEffect
  • 代码行数减少(11行删除,9行新增)
  • 逻辑更集中

✅ 可维护性

  • 状态更新逻辑集中在handleCoinToggle
  • 更容易理解数据流
  • 更容易调试

✅ 类型安全

  • 使用函数式更新(prev => ...
  • TypeScript类型检查通过

🟡 改进建议(非 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在循环中调用

  • ✅ 本PR没有这个问题

陷阱2:useEffect依赖数组遗漏

  • ✅ 移除了useEffect,问题不存在

陷阱3:状态更新不是原子操作

  • ✅ 使用函数式更新(prev => ...),确保原子性

陷阱4:直接修改state

  • ✅ 使用展开运算符创建新数组/对象

总结

这是一个重要的bug修复 PR

优点

  1. ✅ 修复循环依赖问题(关键bug)
  2. ✅ 简化状态管理逻辑
  3. ✅ 提升性能(减少渲染次数)
  4. ✅ 符合React最佳实践
  5. ✅ 代码更简洁(-11行 +9行)
  6. ✅ 更容易维护和调试

改进空间(非 BLOCKING)

  1. ⚠️ 添加注释(解释状态同步逻辑)
  2. ⚠️ 考虑useReducer(如果逻辑复杂化)
  3. ⚠️ 提取常量(SYMBOL_SEPARATOR)
  4. ⚠️ 添加单元测试(验证行为)

审查结论:✅ 通过,强烈建议合并

重要性

影响范围

  • Trading symbols输入功能
  • 状态管理逻辑
  • 低风险改动(简化而非复杂化)

@tangmengqiu tangmengqiu merged commit 1502b7b into NoFxAiOS:dev Nov 10, 2025
29 of 31 checks passed
tanghui315 pushed a commit to tanghui315/nofx-rl that referenced this pull request Nov 10, 2025
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)
sparrow211 pushed a commit to sparrow211/nofx that referenced this pull request Nov 10, 2025
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]>
@the-dev-z the-dev-z deleted the fix/trading-symbols-input-upstream branch November 12, 2025 08:37
bebest2010 pushed a commit to bebest2010/nofx that referenced this pull request Nov 18, 2025
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]>
tinkle-community added a commit that referenced this pull request Nov 26, 2025
#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]>
tinkle-community added a commit that referenced this pull request Nov 26, 2025
#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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] 交易币种界面,无法输入,间隔多个品种

3 participants