Skip to content

SelectInput navigation indicator not updating on arrow keys #18

Description

@carlospuente93

Bug Report: SelectInput navigation indicator not updating on arrow keys

Component

SelectInput from @orchetron/storm

Description

When using SelectInput with keyboard navigation (↑/↓), the highlight indicator does not move visually in the terminal, even though the internal selection state updates correctly.

Root Cause Analysis

1. The Problem

SelectInput uses a useRef (highlightRef) to track the highlighted index:

const highlightRef = useRef(initialIndex);

The highlighted index is computed during React's render:

const effectiveIndex = filteredItems.length === 0 ? 0 : highlightRef.current;

2. What Happens on Key Press

When the user presses ↑/↓:

  1. handleInput updates highlightRef.current
  2. handleInput calls requestRender()

3. The Bug: requestRender()fastRepaint()

requestRender() maps to pipeline.scheduleFastRepaint(), which calls fastRepaint(). This is a custom terminal repaint that:

  • Skips React reconciliation entirely
  • Only repaints the existing committed fiber tree
  • Does not re-run component functions

Since effectiveIndex is computed during React's render (not during repaint), the indicator never updates visually even though highlightRef.current has changed.

4. Compounding Factor: React.memo

SelectInput is wrapped in React.memo. Even if the parent re-renders, React.memo prevents SelectInput from re-rendering because the props (items, onSelect, etc.) have stable references.

Expected Behavior

The highlight indicator should move immediately when the user presses ↑/↓.

Actual Behavior

The highlight indicator stays at the initial position. The internal state updates correctly (selection works), but the visual feedback is missing.

Workaround Used

Force React.memo to allow re-renders by changing a prop on every keypress:

const [navVersion, bumpNav] = useReducer((x: number) => x + 1, 0);

useInput(
  (event) => {
    if (event.key === 'up' || event.key === 'down' || ...) {
      bumpNav();
    }
  },
  { isActive: visible },
);

<SelectInput
  items={items}
  onSelect={onSelect}
  aria-label={String(navVersion)}  // Forces re-render on change
/>

Suggested Fix

Either:

  1. Use state instead of ref for highlightRef so React naturally re-renders on change
  2. Call a full React render instead of fastRepaint() when the highlight changes
  3. Remove React.memo from SelectInput (though this has performance implications)

Environment

  • @orchetron/storm version: 0.2.0
  • Node version: v22.14.0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions