🧹 Refactor TagAutocompleteInput component into smaller parts#971
Hidden character warning
🧹 Refactor TagAutocompleteInput component into smaller parts#971is0692vs wants to merge 2 commits into
Conversation
Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
|
Warning Review limit reached
More reviews will be available in 33 minutes and 7 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
このリポジトリでは staging 先行フローを採用しています。PR のターゲットを |
There was a problem hiding this comment.
Code Review
This pull request refactors the TagAutocompleteInput component by modularizing its UI and logic into separate components (AutocompleteDropdown, TagChips) and a custom hook (useTagSuggestions). The review feedback recommends enforcing a maximum query length limit to prevent database stress from excessively long inputs, and verifying that the input element is still focused before opening the dropdown to avoid race conditions after a blur event.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if (currentTrimmed.length < MIN_QUERY_LENGTH) { | ||
| setLoading(false); | ||
| setSuggestions([]); | ||
| return; | ||
| } |
There was a problem hiding this comment.
To prevent unnecessary processing load and database stress from excessively long inputs, we should enforce a maximum length limit on the search query parameter before making the API request. Additionally, use a pre-defined constant (e.g., MAX_QUERY_LENGTH) instead of hardcoding the value to improve maintainability.
| if (currentTrimmed.length < MIN_QUERY_LENGTH) { | |
| setLoading(false); | |
| setSuggestions([]); | |
| return; | |
| } | |
| if (currentTrimmed.length < MIN_QUERY_LENGTH || currentTrimmed.length > MAX_QUERY_LENGTH) { | |
| setLoading(false); | |
| setSuggestions([]); | |
| return; | |
| } |
References
- Search query parameters should have a maximum length limit (e.g., 100 characters) to prevent unnecessary processing load and database stress. This validation should be performed before any database operations to ensure the system does not process potentially malicious or inefficiently long strings.
- Use pre-defined constants for validation logic instead of hardcoding values to improve maintainability.
| useEffect(() => { | ||
| setHighlightedIndex(suggestions.length > 0 ? 0 : -1); | ||
| setOpen(suggestions.length > 0); | ||
| }, [suggestions]); |
There was a problem hiding this comment.
To prevent the autocomplete dropdown from unexpectedly popping open after the input has already been blurred (e.g., if a slow API request resolves after the user tabs away), we should verify that the input element is still the currently focused element before setting open to true.
useEffect(() => {
setHighlightedIndex(suggestions.length > 0 ? 0 : -1);
if (document.activeElement === document.getElementById(id)) {
setOpen(suggestions.length > 0);
}
}, [suggestions, id]);
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
| useEffect(() => { | ||
| setHighlightedIndex(suggestions.length > 0 ? 0 : -1); | ||
| setOpen(suggestions.length > 0); | ||
| }, [suggestions]); |
There was a problem hiding this comment.
open / aria-expanded が一瞬ずれるレンダリングが発生します
suggestions の更新と open・highlightedIndex の更新が別々の useEffect に分離されたため、suggestions が [] に変化した際に「open=true かつ suggestions=[]」という一時的な状態が 1 フレーム存在します。この間、AutocompleteDropdown の内部ガード (if (suggestions.length === 0) return null) によって視覚的な表示は抑制されますが、<input> の aria-expanded="true" は維持されたままになります。スクリーンリーダーはドロップダウンが開いているとアナウンスしてしまう可能性があります。元のコードでは suggestions・open・highlightedIndex がすべて同一の useEffect 内で同時更新されていたため、この問題は発生しませんでした。open の制御を useTagSuggestions フックに含めるか、open の判定を open && suggestions.length > 0 に戻すことで回避できます。
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/src/components/tag-autocomplete-input.tsx
Line: 58-61
Comment:
**`open` / `aria-expanded` が一瞬ずれるレンダリングが発生します**
`suggestions` の更新と `open`・`highlightedIndex` の更新が別々の `useEffect` に分離されたため、`suggestions` が `[]` に変化した際に「`open=true` かつ `suggestions=[]`」という一時的な状態が 1 フレーム存在します。この間、`AutocompleteDropdown` の内部ガード (`if (suggestions.length === 0) return null`) によって視覚的な表示は抑制されますが、`<input>` の `aria-expanded="true"` は維持されたままになります。スクリーンリーダーはドロップダウンが開いているとアナウンスしてしまう可能性があります。元のコードでは `suggestions`・`open`・`highlightedIndex` がすべて同一の `useEffect` 内で同時更新されていたため、この問題は発生しませんでした。`open` の制御を `useTagSuggestions` フックに含めるか、`open` の判定を `open && suggestions.length > 0` に戻すことで回避できます。
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| highlightedIndex, | ||
| onSelect, | ||
| }: AutocompleteDropdownProps) { | ||
| if (suggestions.length === 0) return null; |
There was a problem hiding this comment.
この早期リターンは防衛的コードではなく正確性のために必要です
親コンポーネントでは {open && <AutocompleteDropdown .../>} という条件付きレンダリングが行われていますが、open と suggestions は別々の useEffect によって更新されるため、open=true かつ suggestions=[] という過渡的な状態が存在します(suggestions が空になった直後の 1 レンダリング間)。この行はその過渡状態を防ぐために実際に機能しています。コードの意図を将来の読者に伝えるために、コメントを追加することをお勧めします。
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/src/components/tag-autocomplete-input/autocomplete-dropdown.tsx
Line: 14
Comment:
**この早期リターンは防衛的コードではなく正確性のために必要です**
親コンポーネントでは `{open && <AutocompleteDropdown .../>}` という条件付きレンダリングが行われていますが、`open` と `suggestions` は別々の `useEffect` によって更新されるため、`open=true` かつ `suggestions=[]` という過渡的な状態が存在します(suggestions が空になった直後の 1 レンダリング間)。この行はその過渡状態を防ぐために実際に機能しています。コードの意図を将来の読者に伝えるために、コメントを追加することをお勧めします。
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
🎯 What: Extracted the complex data-fetching and debouncing logic from
TagAutocompleteInputinto a newuseTagSuggestionshook. Extracted the suggestions dropdown list intoAutocompleteDropdownand the selected tags display intoTagChipscomponents.💡 Why:
TagAutocompleteInputhad grown quite large (>240 lines) because it tightly coupled state management for API requests, debouncing timers, caching, and complex DOM rendering into a single monolithic component. By breaking it into a hook and smaller child components, the core input logic is isolated, making the component significantly easier to maintain, test, and read.✅ Verification:
TagAutocompleteInput(bun run test apps/web/src/components/__tests__/tag-autocomplete-input.test.tsx).bun x vitest run apps/web).bun x tscandbun x prettier.✨ Result: The
TagAutocompleteInputis now significantly shorter and focused purely on rendering the input field and keyboard navigation state, delegating suggestions fetching and child rendering to specialized, single-responsibility functions.PR created automatically by Jules for task 7670127203445948716 started by @is0692vs
Greptile Summary
240行超の
TagAutocompleteInputコンポーネントから、データ取得・デバウンス・キャッシュロジックをuseTagSuggestionsフックへ、ドロップダウンUIをAutocompleteDropdown、タグチップ表示をTagChipsコンポーネントへ抽出するリファクタリングです。useTagSuggestionsは元の実装からデバウンス・キャッシュ・競合リクエスト防止ロジックをそのまま移植し、機能面は同等に保たれています。openとhighlightedIndexの更新がsuggestions変化とは別のuseEffect([suggestions])で行われるようになったため、suggestionsが空になった直後の 1 レンダリングでopen=trueかつsuggestions=[]という過渡的状態が発生します。AutocompleteDropdown内のガードがこれを視覚的に抑制していますが、aria-expandedは一瞬trueのままになります。TagChipsへの抽出はシンプルで問題なく、元のインラインJSXと動作は同等です。Confidence Score: 4/5
全体的にクリーンなリファクタリングで、元の動作はほぼ完全に保たれています。
open状態の更新タイミングが 1 レンダリング遅れる設計上の変化がありますが、通常利用では視覚的に問題になりません。フェッチ・デバウンス・競合制御ロジックは元の実装から忠実に移植されており、
useTagSuggestions単体の品質は高いです。ただしsuggestionsとopen/highlightedIndexの更新を別useEffectに分離したことで、suggestions が空になる際にaria-expanded=trueが一瞬残る過渡的な状態が生まれました。AutocompleteDropdown内のガードが視覚的な問題を防いでいますが、このガードが必要な理由がコードから読み取りづらく、将来の変更で誤って削除されるリスクがあります。open/highlightedIndexの更新ロジックが集中しているtag-autocomplete-input.tsxのuseEffect([suggestions])周辺と、内部ガードの意図が不明確なautocomplete-dropdown.tsxに注意してください。Important Files Changed
useTagSuggestionsフックへ、レンダリングを子コンポーネントへ委譲し大幅にスリム化。open/highlightedIndexの制御がuseEffect([suggestions])に分離されたため、過渡的にaria-expandedが不正になる可能性あり。requestIdRefによる競合制御も維持されており、動作は元コードと同等。suggestions.length === 0ガードは防衛的コードに見えるが、実際には2段階更新の過渡状態を防ぐために必要。コメントがあると意図が明確になる。Sequence Diagram
sequenceDiagram participant User as ユーザー入力 participant TAI as TagAutocompleteInput participant Hook as useTagSuggestions participant API as /api/tags/suggest participant AD as AutocompleteDropdown participant TC as TagChips User->>TAI: テキスト入力 TAI->>Hook: currentTrimmed, orgSlug を渡す Hook->>Hook: MIN_QUERY_LENGTH チェック Hook->>Hook: キャッシュ確認 alt キャッシュヒット Hook-->>TAI: suggestions (キャッシュ) else キャッシュミス Hook->>Hook: debounce (300ms) Hook->>API: "GET /api/tags/suggest?q=..." API-->>Hook: "{ tags: string[] }" Hook->>Hook: キャッシュ保存 Hook-->>TAI: suggestions (新規) end TAI->>TAI: useEffect([suggestions]) Note over TAI: setOpen / setHighlightedIndex TAI->>AD: "open && suggestions, highlightedIndex" TAI->>TC: displayChipsPrompt To Fix All With AI
Reviews (1): Last reviewed commit: "🧹 Refactor TagAutocompleteInput compone..." | Re-trigger Greptile