Skip to content

Pull Request: Frontend Architecture Refactoring & Stability Improvements#74

Merged
amemya merged 18 commits into
mainfrom
refactor/app-hooks
Jun 20, 2026
Merged

Pull Request: Frontend Architecture Refactoring & Stability Improvements#74
amemya merged 18 commits into
mainfrom
refactor/app-hooks

Conversation

@amemya

@amemya amemya commented Jun 20, 2026

Copy link
Copy Markdown
Owner

概要 (Overview)

巨大化していた App.tsx のモノリシックな状態管理とロジックを解体し、関心事ごとに分離されたカスタムフックへ移行する大規模リファクタリングを実施しました。
これに伴い、Reactのライフサイクルに関連する複数の潜在的なバグ(無限ループ、ステートの巻き戻り、メモリリーク等)を発見・修正し、アプリケーションの安定性とパフォーマンスを大幅に向上させています。

主な変更点 (Changes)

1. フックベースのアーキテクチャへの移行

App.tsx に集中していた処理を以下の5つのカスタムフックに分割しました:

  • useImageManager: 読み込み画像のステート(配列)、プレビュー選択、画像個別のメタデータ(EXIF、フレーム色、テキスト色、縦横の向き)の管理。
  • useSettingsSync: Wailsバックエンドとのグローバル設定(自動書き出しデフォルト等)の同期と状態管理。
  • useExport: 単一画像・全画像のバッチエクスポート処理、およびバックエンドへのPOST通信。
  • useBackgroundProcessor: フォルダ監視によるバックグラウンド画像処理イベントの検知と、ユーザーの操作を妨げないサイレント自動保存。
  • useUpdater: アプリケーションの自動アップデート確認・適用ロジックの管理。

2. 重大なバグ修正 (Bug Fixes)

  • ステート強制リセットバグの修正: アスペクト比などを変更した直後に設定が初期値に戻ってしまうバグを修正。イベントリスナー内の関数依存(setExif)による再レンダリング・ループを useRef パターンを用いて遮断しました。
  • 縦横比(Orientation)固定バグの修正: 縦構図の比率を選択しても強制的に横構図(4:3等)として描画されていた問題を修正。グローバル設定ではなく画像個別の向きステートをCanvas描画に渡すようパイプラインを修正しました。
  • UI表示の不整合修正: エクスポートフォルダ選択をキャンセルした際、不必要にローディング状態が書き換わってしまう挙動を整理しました。
  • Strict Mode違反の解消: レンダーフェーズ中に直接 useRef.current を書き換えていたアンチパターンを useLayoutEffect 内に移動し、React Concurrent Mode への安全性を確保しました。

3. パフォーマンスとメモリ最適化 (Optimizations)

  • バッチエクスポート時のメモリ肥大化防止: 多数の画像をループ処理でエクスポートする際、使い終わったオフスクリーンCanvas(width=0, height=0)と動的ロードした Image 要素(src="")を finally ブロックで即座に解放し、メモリリークとスパイクを防止しました。
  • アンマウント時のリソース破棄: バックグラウンド処理中にコンポーネントが破棄された際、未完了の画像ロードを安全にキャンセルする処理を追加しました。
  • 不要な再レンダリングの抑制: useEffectuseCallback の依存配列において、オブジェクト全体(globalExifなど)を渡すのをやめ、実際に使用しているプリミティブ値のみを展開して渡すことで、コールバックの不要な再生成を防ぎました。

4. ネットワークとエラーハンドリング強化 (Network & Error Handling)

  • タイムアウトの実装: バックエンドとの画像保存通信(fetch)に対し、AbortController を用いて15秒のタイムアウトを設定し、サーバー無応答時にUIが無期限にフリーズする問題に対処しました。
  • ログの明瞭化: タイムアウト発生時は AbortError を捕捉し、単なる通信エラーではなく「Timeout」として明示的にログを出力するように改善しました。

影響範囲 (Impact)

  • UIの見た目や操作感に変更はありません(透過的なリファクタリング)。
  • React開発者ツール等で観測した場合の不要な再レンダリング回数が劇的に減少し、特にバッチ書き出し時のメモリ安定性が向上しています。

resolved #71

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 20e4811a-35d6-45dc-b948-e3fc1298b352

📥 Commits

Reviewing files that changed from the base of the PR and between 5e39032 and b032b21.

📒 Files selected for processing (1)
  • frontend/src/hooks/useBackgroundProcessor.ts

📝 Walkthrough

Summary by CodeRabbit

リリースノート

  • Refactor

    • アプリケーション内部構造を再編成し、機能管理をカスタムフックに統合化。
    • 画像管理、設定同期、エクスポート、アップデーター機能をモジュール化。
    • バックグラウンド画像処理パイプラインを実装し、処理効率を向上。
  • Bug Fixes

    • 初期化順序の問題を修正し、設定反映の安定性を改善。

Walkthrough

App.tsx 内のローカル状態・処理ロジックを useSettingsSyncuseImageManageruseExportuseUpdateruseBackgroundProcessor の5つのカスタムフックへ抽出した。ImportedImage 型に orientation フィールドを追加し、App.tsx は各フックの呼び出しと UI バインディングのみに簡素化された。

Changes

App.tsx フック分離リファクタリング

Layer / File(s) Summary
ImportedImage 型拡張と useSettingsSync 契約定義
frontend/src/types.ts, frontend/src/hooks/useSettingsSync.ts
ImportedImageorientation フィールドを追加。useSettingsSyncGetSettings による初期ロード・settings_saved イベント購読・handleSaveAutoExportDefault による SaveSettings 呼び出しを実装した。
useImageManager の状態管理・画像ロード・EXIF 適用
frontend/src/hooks/useImageManager.ts
importedImages / selectedIndex / isCanvasReady / globalExif の state と派生値、per-image の EXIF・カラー更新コールバック、imageURL の Image プリロード、handleExifResults / handleApplyToAll / handleApplyColorsToAll を実装した。
useUpdater の更新状態管理と操作関数
frontend/src/hooks/useUpdater.ts
UpdateStage / UpdateState 型定義と useEffect 内での CheckForUpdate およびダウンロード進捗・エラー等イベント購読、triggerUpdate / restartApp / dismissUpdateError 操作関数を実装した。
useExport の単一/バッチエクスポートフロー
frontend/src/hooks/useExport.ts
uploadCanvasBlob ヘルパー(15秒タイムアウト付き)、downloadImageSaveImage + アップロード)、downloadAllImagesSelectExportFolder + 多重実行抑止 + SaveBatchImage)を実装した。
useBackgroundProcessor の自動保存バックグラウンド処理
frontend/src/hooks/useBackgroundProcessor.ts
process_file イベント購読、Image ロード → GetSettings → EXIF 合成 → renderImageToCanvastoBlobSaveAutoImage + POST /api/save の自動保存フローとアンマウント時クリーンアップを実装した。
App.tsx のフック接続と UI バインディング更新
frontend/src/App.tsx
setExifRef 遅延参照設定、files-dropped / handleOpenImages の委譲先を imageManager.handleExifResults へ変更し、キャンバス描画・プレビュー・フィルムストリップ・サイドバー・エクスポートボタン・更新ボタンを各フックのプロパティ/メソッドへ全面切り替えした。

Sequence Diagram(s)

sequenceDiagram
  rect rgba(173, 216, 230, 0.5)
    Note over App,useImageManager: 画像取り込みフロー
    App->>useImageManager: handleExifResults(ExifResult[])
    useImageManager->>useImageManager: importedImages 差し替え / selectedIndex リセット
    useImageManager->>App: imageObj (Image プリロード完了)
    App->>App: renderImageToCanvas(imageManager.imageObj, imageManager.exif, ...)
  end
  rect rgba(255, 218, 185, 0.5)
    Note over App,useBackgroundProcessor: バックグラウンド自動保存フロー
    WailsEvents->>useBackgroundProcessor: process_file イベント
    useBackgroundProcessor->>AppAPI: GetSettings()
    AppAPI-->>useBackgroundProcessor: Settings
    useBackgroundProcessor->>useBackgroundProcessor: renderImageToCanvas (オフスクリーン)
    useBackgroundProcessor->>AppAPI: SaveAutoImage(token)
    useBackgroundProcessor->>SaveServer: POST /api/save?token=...
  end
  rect rgba(144, 238, 144, 0.5)
    Note over App,useUpdater: アップデートフロー
    useUpdater->>AppAPI: CheckForUpdate()
    AppAPI-->>useUpdater: updateState.stage 更新
    App->>useUpdater: triggerUpdate()
    useUpdater->>AppAPI: TriggerUpdate()
    App->>useUpdater: restartApp()
    useUpdater->>AppAPI: RestartApp()
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • amemya/ExifFrame#63: OpenImageshandleExifResults のバッチインポート・フィルムストリップ選択・downloadAllImages のマルチ画像エクスポートパスを App.tsx に追加した PR で、本 PR でその処理が useImageManager / useExport へ分離された。
  • amemya/ExifFrame#70: renderImageToCanvas / getExportInfo / getQualityFromBPP ユーティリティを抽出した PR で、本 PR の useBackgroundProcessor / useExport はそれらのユーティリティを直接呼び出している。
  • amemya/ExifFrame#68: JPEG 品質算出ロジック getQualityFromBPP を導入した PR で、本 PR の useExportuseBackgroundProcessor でその品質値を canvas.toBlob の品質パラメータへ反映している。
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed タイトルは「Frontend Architecture Refactoring & Stability Improvements」であり、PR全体の主要な変更である大規模リファクタリングと安定性向上を的確に要約している。変更内容と完全に関連しており、読者がPRの目的を一目で理解できる。
Description check ✅ Passed 詳細な説明文は、App.tsxのモノリシック状態管理の5つのカスタムフックへの分割、複数のバグ修正、パフォーマンス最適化、ネットワークハンドリング強化など、実装内容と完全に対応している。
Linked Issues check ✅ Passed リンクされたIssue #71は、App.tsx内のロジックを useBackgroundProcessor、useImageManager、useExport、useSettingsSyncの4つフックに分離することを要求しており、このPRは要求された4つすべてのフックと追加のuseUpdaterを実装して完全に満たしている。
Out of Scope Changes check ✅ Passed すべての変更は、Issue #71が要求するフック分離と、それに伴うリファクタリング内で必要とされる範囲内にある。バグ修正やパフォーマンス最適化は、フック化を進める過程で発見・対処された合理的な改善である。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/app-hooks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/hooks/useBackgroundProcessor.ts`:
- Around line 56-140: The offscreenCanvas created at the beginning of this code
block is never explicitly released after use, which can cause memory leaks when
processing multiple files in the background. After the blob upload completes
(either successfully or with an error) in the toBlob callback, you need to
release the canvas resources by clearing its context and removing references to
it. Add canvas cleanup logic at the end of the toBlob callback, ensuring it runs
in both success and failure paths, similar to how it's handled in the
downloadAllImages function using a finally block pattern.

In `@frontend/src/hooks/useSettingsSync.ts`:
- Around line 36-41: The assignment `setExifRef.current = setExif` is being
performed directly during render, but the PR goal requires moving useRef.current
direct modifications to useLayoutEffect to resolve React Strict Mode violations.
Move the `setExifRef.current = setExif` assignment from the render body into a
new useLayoutEffect hook with `setExif` as a dependency. This ensures
consistency with the pattern used in App.tsx and aligns with the PR's approach
for handling ref updates safely.

In `@frontend/src/hooks/useUpdater.ts`:
- Around line 16-17: The comment above the getEventData helper function has
unnecessary indentation (4 spaces) that doesn't align with module-scope code
conventions. Remove the leading indentation from the comment that precedes the
getEventData function definition so that it aligns with the function declaration
itself at the module scope level.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 80c16f7d-2fd2-47a1-97cf-c01877f797c2

📥 Commits

Reviewing files that changed from the base of the PR and between 9355c63 and 74d2faa.

📒 Files selected for processing (7)
  • frontend/src/App.tsx
  • frontend/src/hooks/useBackgroundProcessor.ts
  • frontend/src/hooks/useExport.ts
  • frontend/src/hooks/useImageManager.ts
  • frontend/src/hooks/useSettingsSync.ts
  • frontend/src/hooks/useUpdater.ts
  • frontend/src/types.ts

Comment thread frontend/src/hooks/useBackgroundProcessor.ts
Comment thread frontend/src/hooks/useSettingsSync.ts Outdated
Comment thread frontend/src/hooks/useUpdater.ts Outdated
@amemya

amemya commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/hooks/useBackgroundProcessor.ts`:
- Around line 103-106: The code in useBackgroundProcessor.ts silently returns
when blob is null without any logging, making it difficult to debug when toBlob
fails due to canvas contamination or other issues. Add a console warning or
logger call in the condition where blob is null (the `if (!blob) return;`
statement) to log the issue before returning, so that debugging becomes easier
when this null case occurs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 519f624a-48fa-4a4a-b0c9-d69a9c401387

📥 Commits

Reviewing files that changed from the base of the PR and between 74d2faa and 5e39032.

📒 Files selected for processing (4)
  • frontend/src/hooks/useBackgroundProcessor.ts
  • frontend/src/hooks/useExport.ts
  • frontend/src/hooks/useSettingsSync.ts
  • frontend/src/hooks/useUpdater.ts

Comment thread frontend/src/hooks/useBackgroundProcessor.ts Outdated
@amemya

amemya commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@amemya amemya merged commit 847ad12 into main Jun 20, 2026
1 check passed
@amemya amemya deleted the refactor/app-hooks branch June 20, 2026 20:45
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.

refactor: Separate the hooks in app.tsx

1 participant