Skip to content

refactor(hex-viewer): split monolith, fix React anti-patterns, add tests & CI - #1

Merged
malsuke merged 2 commits into
mainfrom
refactor/component-architecture-tests-ci
Jun 29, 2026
Merged

refactor(hex-viewer): split monolith, fix React anti-patterns, add tests & CI#1
malsuke merged 2 commits into
mainfrom
refactor/component-architecture-tests-ci

Conversation

@malsuke

@malsuke malsuke commented Jun 28, 2026

Copy link
Copy Markdown
Owner

概要

バイナリビュワー HexViewer のReactコンポーネントとしての品質を改善し、ユニットテストとCIを追加しました。

React的な問題の修正

  • レンダリング中の ref 書き込みを撤廃: cursorRef.current = cursor のように state を ref にミラーしてレンダー中に書き込んでいた箇所(react-hooks/refs で4件のlintエラー)を削除。ハンドラは state を直接参照する形へ。
  • effect内の同期 setState を撤廃: hexString 変更時のリセットを、React公式推奨の「前回propをstateに持ちレンダー中に調整」パターンへ(react-hooks/set-state-in-effect の解消)。
  • 約500行のモノリスを責務ごとに分割:
    • src/lib/hex.ts — パース/フォーマット/数値解釈の純粋関数
    • src/lib/cx.ts — className 結合ヘルパー
    • src/hooks/useHexEditor.ts — 編集状態と操作
    • src/hooks/useVirtualRows.ts — 仮想スクロール
    • src/components/HexHeader / HexRow(memo) / StatusBar / DebugPanel
  • HexRow を memo 化 し primitive な props を渡すことで、カーソル移動や1バイト編集で該当行のみ再描画。
  • 新バッファ読込時にビューポートを実DOMごと先頭へ戻す(state/DOMのズレを修正)。

挙動は元と等価になるよう保持(nbsp、Float32のtoPrecision(7)表示、選択/カーソル判定など)。

テスト & CI

  • 純粋ロジック(hex.ts / cx.ts)のユニットテスト23ケースを追加。vitestに unit(node環境)プロジェクトを新設し、既存の Storybook ブラウザプロジェクトと分離 → Playwright不要で高速・安定。
  • scripts追加: test / test:watch / test:storybook / test:all / typecheck
  • .github/workflows/ci.yaml: linttypecheckbuild → unit test、別ジョブでPlaywright導入の上 Storybook インタラクションテスト。

ローカル検証

  • pnpm lint → 0 エラー(before: 5エラー)
  • pnpm typecheck → 成功
  • pnpm build → 成功
  • pnpm test(unit)→ 23/23 passed
  • pnpm test:storybook(実ブラウザ)→ 6/6 passed

🤖 Generated with Claude Code

malsuke and others added 2 commits June 28, 2026 17:25
…sts & CI

Component quality:
- Remove ref-during-render mirroring (cursor/nibble/selection/data refs) that
  violated React purity and tripped react-hooks/refs lint errors. Handlers now
  read state directly; stability is unnecessary for JSX event props.
- Replace the setState-in-effect reset on hexString change with the
  recommended "adjust state from prev prop during render" pattern.
- Split the ~500-line component into focused units:
  - src/lib/hex.ts        pure parse/format/interpret helpers
  - src/lib/cx.ts         className join helper
  - src/hooks/useHexEditor.ts   editor state + interactions
  - src/hooks/useVirtualRows.ts windowed rendering
  - src/components/*      HexHeader, HexRow (memoized), StatusBar, DebugPanel
- Memoize rows with primitive props so navigation/edits re-render only the
  affected rows. Hoist the hex-key RegExp out of the handler.
- Scroll the viewport back to top on new buffers (fixes state/DOM desync).

Tests & CI:
- Add Node unit tests for the pure logic (hex.ts, cx.ts) under a dedicated
  vitest "unit" project; keep the Storybook browser project separate.
- Add scripts: test, test:watch, test:storybook, test:all, typecheck.
- Add .github/workflows/ci.yaml running lint, typecheck, build and unit tests,
  plus a separate Playwright job for Storybook interaction tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…xample/)

eslint-plugin-react-hooks v7 needs project info; with both the root and
example/ tsconfigs as candidates the parser failed in CI with 'No
tsconfigRootDir was set'. Pin tsconfigRootDir to the repo root and exclude the
standalone example app from the root lint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@malsuke
malsuke merged commit 654299c into main Jun 29, 2026
2 checks passed
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.

1 participant