SKILL.md
唯讀
名稱
typescript-react-reviewer
描述
TypeScript + React 19 應用程式的專業程式碼審查專家。用於審查 React 程式碼、識別反模式、評估狀態管理或評估程式碼可維護性。觸發時機:程式碼審查請求、PR 審查、React 架構評估、識別程式碼異味、TypeScript 型別安全檢查、useEffect 濫用偵測、狀態管理審查。
TypeScript + React 19 程式碼審查專家
具備 React 19 新功能、TypeScript 最佳實務、狀態管理模式與常見反模式深厚知識的專業程式碼審查專家。
審查優先等級
🚫 嚴重(阻擋合併)
這些問題會導致錯誤、記憶體洩漏或架構問題:
| 問題 | 為何嚴重 |
|---|---|
useEffect 用於衍生狀態 |
額外的渲染週期、同步錯誤 |
useEffect 缺少清理 |
記憶體洩漏 |
直接修改狀態(.push()、.splice()) |
靜默更新失敗 |
| 條件式呼叫 Hook | 違反 Hook 規則 |
動態列表中使用 key={index} |
重新排序時狀態損毀 |
未說明理由的 any 型別 |
繞過型別安全 |
在與 <form> 相同的元件中使用 useFormStatus |
永遠回傳 false(React 19 錯誤) |
在渲染期間使用 use() 建立 Promise |
無限迴圈 |
⚠️ 高優先
| 問題 | 影響 |
|---|---|
| 依賴陣列不完整 | 過時閉包、更新遺漏 |
Props 型別為 any |
執行時期錯誤 |
無正當理由的 useMemo/useCallback |
不必要的複雜度 |
| 缺少 Error Boundary | 錯誤使用者體驗不佳 |
受控輸入初始化為 undefined |
React 警告 |
📝 架構/風格
| 問題 | 建議 |
|---|---|
| 元件超過 300 行 | 拆分為較小的元件 |
| Prop drilling 超過 2-3 層 | 使用組合或 context |
| 狀態離使用位置太遠 | 將狀態就近放置 |
自訂 Hook 未以 use 開頭 |
遵循命名慣例 |
快速偵測模式
useEffect 濫用(最常見的反模式)
// ❌ 錯誤:在 useEffect 中衍生狀態
const [firstName, setFirstName] = useState('');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
// ✅ 正確:在渲染期間計算
const fullName = firstName + ' ' + lastName;
// ❌ 錯誤:在 useEffect 中處理事件邏輯
useEffect(() => {
if (product.isInCart) showNotification('Added!');
}, [product]);
// ✅ 正確:在事件處理器中處理邏輯
function handleAddToCart() {
addToCart(product);
showNotification('Added!');
}
React 19 Hook 錯誤
// ❌ 錯誤:在表單元件中使用 useFormStatus(永遠回傳 false)
function Form() {
const { pending } = useFormStatus();
return <form action={submit}><button disabled={pending}>Send</button></form>;
}
// ✅ 正確:在子元件中使用 useFormStatus
function SubmitButton() {
const { pending } = useFormStatus();
return <button type="submit" disabled={pending}>Send</button>;
}
function Form() {
return <form action={submit}><SubmitButton /></form>;
}
// ❌ 錯誤:在渲染期間建立 Promise(無限迴圈)
function Component() {
const data = use(fetch('/api/data')); // 每次渲染都是新的 Promise!
}
// ✅ 正確:從 props 或 state 取得 Promise
function Component({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise);
}
狀態修改偵測
// ❌ 錯誤:直接修改(不會觸發重新渲染)
items.push(newItem);
setItems(items);
arr[i] = newValue;
setArr(arr);
// ✅ 正確:不可變更新
setItems([...items, newItem]);
setArr(arr.map((x, idx) => idx === i ? newValue : x));
TypeScript 危險訊號
// ❌ 要抓到的危險訊號
const data: any = response; // 不安全的 any
const items = arr[10]; // 缺少 undefined 檢查
const App: React.FC<Props> = () => {}; // 不建議的模式
// ✅ 建議的模式
const data: ResponseType = response;
const items = arr[10]; // 搭配 noUncheckedIndexedAccess
const App = ({ prop }: Props) => {}; // 明確的 props
審查工作流程
- 先掃描嚴重問題 - 檢查「嚴重(阻擋合併)」區段中的模式
- 檢查 React 19 的使用 - 參閱 react19-patterns.md 了解新的 API 模式
- 評估狀態管理 - 狀態是否就近放置?伺服器狀態與客戶端狀態是否分離?
- 評估 TypeScript 安全性 - 泛型元件、可辨識聯集、嚴格設定
- 審查可維護性 - 元件大小、Hook 設計、資料夾結構
參考文件
如需詳細的模式與範例:
- react19-patterns.md - React 19 新 Hooks(useActionState、useOptimistic、use)、伺服器/客戶端元件邊界
- antipatterns.md - 完整的反模式目錄與修正方法
- checklist.md - 完整的程式碼審查檢查清單,適用於徹底的審查
狀態管理快速指南
| 資料類型 | 解決方案 |
|---|---|
| 伺服器/非同步資料 | TanStack Query(絕不複製到本地狀態) |
| 簡單的全域 UI 狀態 | Zustand(約 1KB,無需 Provider) |
| 精細的衍生狀態 | Jotai(約 2.4KB) |
| 元件本地狀態 | useState/useReducer |
| 表單狀態 | React 19 的 useActionState |
TanStack Query 反模式
// ❌ 絕不要將伺服器資料複製到本地狀態
const { data } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos });
const [todos, setTodos] = useState([]);
useEffect(() => setTodos(data), [data]);
// ✅ Query 就是資料來源
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 |
應用程式程式碼中的 Barrel 檔案(index.ts) |
套件膨脹、循環依賴 | 直接匯入 |






