Skip to content

Refactor useChordState into focused single-concern hooks#264

Merged
JWWade merged 2 commits intodevelopfrom
copilot/issue-e7-03-refactor-usechordstate
Mar 17, 2026
Merged

Refactor useChordState into focused single-concern hooks#264
JWWade merged 2 commits intodevelopfrom
copilot/issue-e7-03-refactor-usechordstate

Conversation

Copy link
Contributor

Copilot AI commented Mar 17, 2026

useChordState was a 363-line monolith mixing drag tracking, chord selection, custom chord manipulation, and screen-reader announcements — untestable in isolation and prone to cross-concern re-renders.

Split

Hook Lines Owns
useDragState 74 Pointer drag state + startDrag / updateDragPosition / resetDrag
useChordSelection 11 selectedChordName only
useCustomChordState 150 customFromChord + rotate / mirror / random / primitive-shape callbacks
useChordState (orchestrator) 150 Composes the three above; owns suppressNextClick, moveAnnouncement, and drag-end chord resolution

Design notes

  • Public interface unchanged — no call sites touched.
  • handleNoteDragMove is re-exported directly as updateDragPosition (already memoized by useDragState), removing a redundant useCallback wrapper.
  • handleNoteDragEnd stays in the orchestrator — it's the one callback that genuinely crosses all three concerns (drag state → chord resolution → announcement).
  • A PRIMITIVE_SHAPE_META const map replaces two inline ternary chains, enabling handleSelectPrimitiveShape to be a few clear lines.
// useChordState.ts is now just composition
const { selectedChordName, setSelectedChordName } = useChordSelection();
const { customFromChord, setCustomFromChord, handleRotateChord, ... } = useCustomChordState({ ... });
const { isDragging, dragTargetIndex, startDrag, updateDragPosition, resetDrag, ... } = useDragState();

const handleNoteDragMove = updateDragPosition; // stable ref, no extra wrapper needed

All three new hooks are exported from chromatic-circle/hooks/index.ts.

Original prompt

This section details on the original issue you should resolve

<issue_title>ISSUE-E7-03 — Refactor useChordState into Focused Single-Concern Hooks</issue_title>
<issue_description># ISSUE-E7-03 — Refactor useChordState into Focused Single-Concern Hooks

Objective

Break the monolithic useChordState hook (309 lines, 7 useState calls, 5 useCallback functions, multiple unrelated concerns) into smaller, independently testable hooks.

Background

client/src/features/chromatic-circle/hooks/useChordState.ts currently manages all of the following in a single function:

Concern State/callbacks
Pointer drag detection dragStart, currentDrag, dragHasMoved
Named chord selection selectedChord, selectChord
Custom chord construction customNotes, toggleCustomNote, clearCustomNotes, isCustomMode
Primitive chord shape derived from selectedChord.type
Screen-reader announcements announcement string, setter

The combined size makes the hook impossible to unit-test (too many internal dependencies) and difficult to reason about (unrelated state changes may trigger re-renders together).

Proposed Split

useDragState (new)

// Owns: dragStart, currentDrag, dragHasMoved
// Exported from: chromatic-circle/hooks/useDragState.ts

useChordSelection (new)

// Owns: selectedChord, selectChord callback
// Exported from: chromatic-circle/hooks/useChordSelection.ts

useCustomChordState (new)

// Owns: customNotes, toggleCustomNote, clearCustomNotes, isCustomMode
// Exported from: chromatic-circle/hooks/useCustomChordState.ts

useChordState (thin orchestrator — keep same public interface)

// Composes the three new hooks + announcement state
// Public surface unchanged — callers require no edits
// Target: ≤150 lines

Files To Add

  • client/src/features/chromatic-circle/hooks/useDragState.ts
  • client/src/features/chromatic-circle/hooks/useChordSelection.ts
  • client/src/features/chromatic-circle/hooks/useCustomChordState.ts

Files To Edit

  • client/src/features/chromatic-circle/hooks/useChordState.ts — gutted to orchestrator; delegates to new hooks.
  • client/src/features/chromatic-circle/hooks/index.ts — export new hooks if not already done.

Files Not To Edit

  • Any component that calls useChordState — the public interface must remain identical.

Acceptance Criteria

  • Three new focused hooks exist, each ≤150 lines and with a single declared concern.
  • useChordState.ts is ≤150 lines and only composes the three hooks.
  • No caller of useChordState requires any change.
  • All existing tests pass.
  • npm run lint passes with --max-warnings=0.
  • npm run build succeeds with no TypeScript errors.

Verification Commands

cd client
npm run lint
npm run build
npm test
```</issue_description>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>

📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

Copilot AI changed the title [WIP] [ISSUE-E7-03] Refactor useChordState into focused single-concern hooks Refactor useChordState into focused single-concern hooks Mar 17, 2026
Copilot AI requested a review from JWWade March 17, 2026 05:31
@JWWade JWWade marked this pull request as ready for review March 17, 2026 14:01
@JWWade JWWade merged commit 18fe8e3 into develop Mar 17, 2026
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.

ISSUE-E7-03 — Refactor useChordState into Focused Single-Concern Hooks

2 participants