Skip to content

Conversation

@hughescr
Copy link

@hughescr hughescr commented Oct 4, 2025

Problem

When multiple arrow key presses arrived in rapid succession (e.g., from terminal automation or fast typing), only a single navigation step would occur instead of multiple steps.

Root Cause

The useInput handler was using direct state updates:

setSelectedIndex(nextIndex)

This caused React's state batching to consolidate multiple rapid keypresses. All keypresses read the same closure value of selectedIndex before React re-rendered, so they all calculated the same nextIndex, resulting in only one move.

Solution

Use functional setState to ensure each keypress gets the latest index value:

setSelectedIndex(currentIndex => {
  const nextIndex = findNextValidIndex(currentIndex, step)
  // ... handle rotation
  return nextIndex
})

This ensures each navigation handler reads the most recent state value, allowing rapid keypresses to be processed correctly.

Changes

  • Modified src/enhanced-select-input/index.tsx to use functional form of setSelectedIndex
  • Added early returns after state updates to prevent fall-through logic
  • Moved rotation logic inside the setState callback

Testing

Tested with terminal automation sending multiple down arrow keys in quick succession - now correctly moves multiple positions instead of just one.

Impact

This is a bug fix that improves the component's responsiveness for:

  • Users with fast typing speeds
  • Terminal automation tools
  • Accessibility tools that programmatically send keypresses

## Problem
When multiple arrow key presses arrived in rapid succession (e.g., from
terminal automation or fast typing), only a single navigation step would
occur instead of multiple steps.

## Root Cause
The useInput handler was using direct state updates:
```typescript
setSelectedIndex(nextIndex)
```

This caused React's state batching to consolidate multiple rapid keypresses.
All keypresses read the same closure value of `selectedIndex` before React
re-rendered, so they all calculated the same `nextIndex`, resulting in only
one move.

## Solution
Use functional setState to ensure each keypress gets the latest index value:
```typescript
setSelectedIndex(currentIndex => {
  const nextIndex = findNextValidIndex(currentIndex, step)
  // ... handle rotation
  return nextIndex
})
```

This ensures each navigation handler reads the most recent state value,
allowing rapid keypresses to be processed correctly.

## Testing
Tested with terminal automation sending multiple down arrow keys in quick
succession - now correctly moves multiple positions instead of just one.
@hughescr
Copy link
Author

hughescr commented Oct 4, 2025

Closing this PR - testing shows the fix doesn't actually resolve the issue. Rapid keypresses are still being dropped/batched. Need to investigate further to identify the root cause.

@hughescr hughescr closed this Oct 4, 2025
@gfargo
Copy link
Owner

gfargo commented Oct 6, 2025

Hey @hughescr, if you need another set of eyes or would like me to do some of my own testing, I'd be happy to help! Appreciate the contribution 🙌

@hughescr
Copy link
Author

hughescr commented Oct 8, 2025

@gfargo I realized that the problem seems to actually be in Ink itself, not in this component specifically -- I sent a PR there as vadimdemedes/ink#782

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.

2 participants