typescript-react-reviewer について
typescript-react-reviewer は、TypeScript と React 19 のプロジェクト向けコードレビュー技能です。開発者やレビュアーが、フック、状態管理、型安全性、設計上の問題を優先度別に確認でき、チェックリストも参照できます。
主な機能
- 01優先度別の問題判定
- 02React 19 フックの使用確認
- 03状態変更のアンチパターン検出
- 04TypeScript の型安全性評価
- 05レビュー手順とチェックリスト
活用シーン
- 01マージ前の重大な問題の確認
- 02フックの依存配列とクリーンアップ確認
- 03コンポーネント構成と状態管理の評価
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ dotneet/ claude-code-marketplace --skill typescript-react-reviewerインストール後、Claude Code で/typescript-react-reviewerを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/antipatterns.mdchecklist.mdreact19-patterns.md詳細
TypeScript + React 19 コードレビューエキスパート
React 19 の新機能、TypeScript のベストプラクティス、状態管理パターン、よくあるアンチパターンに精通したエキスパートのコードレビュアーです。
レビューの優先度
🚫 重大(マージをブロック)
以下の問題は、バグ、メモリリーク、またはアーキテクチャ上の問題を引き起こします。
| 問題 | 重大である理由 |
|---|---|
派生状態に useEffect を使用 | 余分なレンダーサイクル、同期の不具合 |
useEffect のクリーンアップ漏れ | メモリリーク |
状態の直接変更(.push()、.splice()) | 更新の失敗に気づけない |
| 条件付きフック呼び出し | Rules of Hooks に違反 |
動的リストでの key={index} | 並べ替え時に状態が破損 |
正当な理由のない any 型 | 型安全性が損なわれる |
<form> と同じコンポーネント内での useFormStatus | 常に false を返す(React 19 のバグ) |
use() でレンダー中に Promise を作成 | 無限ループ |
⚠️ 優先度:高
| 問題 | 影響 |
|---|---|
| 依存配列が不完全 | 古いクロージャー、更新漏れ |
any 型の Props | 実行時エラー |
正当な理由のない useMemo/useCallback | 不必要な複雑さ |
| Error Boundary の不足 | エラー発生時の UX が悪化 |
undefined で初期化された制御入力 | React の警告 |
📝 アーキテクチャ/スタイル
| 問題 | 推奨事項 |
|---|---|
| コンポーネントが 300 行を超える | 小さなコンポーネントに分割 |
| Props のバケツリレーが 2~3 階層を超える | コンポジションまたはコンテキストを使用 |
| 状態が使用箇所から遠い | 状態を使用箇所の近くに配置 |
use プレフィックスがないカスタムフック | 命名規則に従う |
よくある検出パターン
useEffect の誤用(最もよくあるアンチパターン)
// ❌ WRONG: Derived state in useEffect
const [firstName, setFirstName] = useState('');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
// ✅ CORRECT: Compute during render
const fullName = firstName + ' ' + lastName;
// ❌ WRONG: Event logic in useEffect
useEffect(() => {
if (product.isInCart) showNotification('Added!');
}, [product]);
// ✅ CORRECT: Logic in event handler
function handleAddToCart() {
addToCart(product);
showNotification('Added!');
}
React 19 のフックに関する誤り
// ❌ WRONG: useFormStatus in form component (always returns false)
function Form() {
const { pending } = useFormStatus();
return <form action={submit}><button disabled={pending}>Send</button></form>;
}
// ✅ CORRECT: useFormStatus in child component
function SubmitButton() {
const { pending } = useFormStatus();
return <button type="submit" disabled={pending}>Send</button>;
}
function Form() {
return <form action={submit}><SubmitButton /></form>;
}
// ❌ WRONG: Promise created in render (infinite loop)
function Component() {
const data = use(fetch('/api/data')); // New promise every render!
}
// ✅ CORRECT: Promise from props or state
function Component({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise);
}
状態変更の検出
// ❌ WRONG: Mutations (no re-render)
items.push(newItem);
setItems(items);
arr[i] = newValue;
setArr(arr);
// ✅ CORRECT: Immutable updates
setItems([...items, newItem]);
setArr(arr.map((x, idx) => idx === i ? newValue : x));
TypeScript の危険信号
// ❌ Red flags to catch
const data: any = response; // Unsafe any
const items = arr[10]; // Missing undefined check
const App: React.FC<Props> = () => {}; // Discouraged pattern
// ✅ Preferred patterns
const data: ResponseType = response;
const items = arr[10]; // with noUncheckedIndexedAccess
const App = ({ prop }: Props) => {}; // Explicit props
レビューワークフロー
- まず重大な問題をスキャンする - 「重大(マージをブロック)」のセクションにあるパターンを確認します
- React 19 の使用方法を確認する - 新しい API パターンについては react19-patterns.md を参照します
- 状態管理を評価する - 状態は使用箇所の近くにありますか? サーバー状態とクライアント状態を分離していますか?
- TypeScript の安全性を評価する - ジェネリックコンポーネント、判別可能なユニオン、厳格な設定を確認します
- 保守性を確認する - コンポーネントのサイズ、フックの設計、フォルダー構成を確認します
参考ドキュメント
詳細なパターンと例については、以下を参照してください。
- react19-patterns.md - React 19 の新しいフック(useActionState、useOptimistic、use)、サーバー/クライアントコンポーネントの境界
- antipatterns.md - 修正方法を含む、包括的なアンチパターンカタログ
- checklist.md - 詳細なレビュー向けの完全なコードレビューチェックリスト
状態管理のクイックガイド
| データの種類 | 解決策 |
|---|---|
| サーバー/非同期データ | TanStack Query(ローカル状態にコピーしない) |
| シンプルなグローバル UI 状態 | Zustand(約 1 KB、Provider 不要) |
| 細粒度の派生状態 | Jotai(約 2.4 KB) |
| コンポーネントローカル状態 | useState/useReducer |
| フォームの状態 | React 19 の useActionState |
TanStack Query のアンチパターン
// ❌ NEVER copy server data to local state
const { data } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos });
const [todos, setTodos] = useState([]);
useEffect(() => setTodos(data), [data]);
// ✅ Query IS the source of truth
const { data: todos } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos });
TypeScript 設定の推奨事項
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"exactOptionalPropertyTypes": true
}
}
noUncheckedIndexedAccess は重要です。arr[i] が undefined を返すケースを検出できます。
すぐに指摘すべき危険信号
レビュー時に、以下のパターンを見つけたらすぐに指摘してください。
| パターン | 問題 | 修正方法 |
|---|---|---|
eslint-disable react-hooks/exhaustive-deps | 古いクロージャーのバグを隠す | ロジックをリファクタリング |
| コンポーネント内でコンポーネントを定義 | レンダーのたびに再マウントされる | コンポーネントの外に移動 |
入力に対する useState(undefined) | 非制御コンポーネントに関する警告 | 空文字列を使用 |
ジェネリクスを指定した React.FC | ジェネリック型の推論が機能しない | Props を明示的に指定 |
アプリケーションコード内のバレルファイル(index.ts) | バンドル肥大化、循環依存 | 直接インポート |
コメント
Markdown 対応