世界上有一种医生,专治代码疑难杂症 —— 它不用听诊器,它用AST
React Doctor,就像代码的AI主治医师。你的Agent写的React代码就像实习生写的处方,看起来能跑,但实际上一塌糊涂。React Doctor就是那个把关的老专家,帮你揪出那些让代码"慢性死亡"的问题。
React Doctor是一个AI驱动的React代码质量检测工具,它能:
React Doctor能识别最常见的性能杀手:
在useEffect中做太多事情,导致不必要的重新渲染
每个useEffect只处理一个副作用,通过依赖数组精确控制
每次渲染都创建新对象/函数,导致子组件无意义重新渲染
缓存计算结果和回调函数
// React Doctor 诊断示例
// 问题代码:把太多状态放在全局
const [state, setState] = useState({
user: null,
cart: [],
products: [],
filters: {},
// ... 20个更多字段
});
// React Doctor 诊断结果:
// ❌ 状态粒度过粗,一个字段变化导致整个组件重新渲染
// ❌ 多个不相关的状态耦合在一起
// ✅ 建议:拆分为多个useState或useReducer
// 修复后:
const [user, setUser] = useState(null);
const [cart, setCart] = useState([]);
const [products, setProducts] = useState([]);
const [filters, setFilters] = useState({});
在OpenClaw中,React Doctor可以作为代码审查环节集成到Agent的工作流中:
# OpenClaw中集成React Doctor
from openclaw import Agent, Tool, Workflow
# 定义React Doctor工具
react_doctor = Tool(
name="react_doctor",
description="检测React代码质量问题",
function=lambda code: diagnose_react(code)
)
# 创建代码开发Agent
developer = Agent(
name="ReactDeveloper",
tools=[react_doctor],
skills=["react_development", "code_review"]
)
# 工作流程:写代码 → 诊断 → 修复
workflow = Workflow([
Step("write_code", developer.write_react_component),
Step("diagnose", react_doctor.run),
Step("fix_issues", developer.fix_code_issues),
Step("verify", react_doctor.run) # 二次验证
])
# 执行工作流
result = workflow.execute(component_requirements)
React Doctor不仅能发现问题,还能自动修复:
# React Doctor 完整诊断系统
import ast
import re
from typing import List, Dict
class ReactDoctor:
def __init__(self):
self.rules = [
self.check_use_effect_deps,
self.check_inline_props,
self.check_state_granularity,
self.check_missing_keys,
self.check_unnecessary_renders
]
def diagnose(self, code: str) -> Dict:
"""执行完整诊断"""
results = {
"score": 100,
"issues": [],
"suggestions": []
}
for rule in self.rules:
issues = rule(code)
for issue in issues:
results["issues"].append(issue)
results["score"] -= issue["severity"]
# 生成修复建议
results["suggestions"] = self._generate_suggestions(results["issues"])
return results
def check_use_effect_deps(self, code: str) -> List[Dict]:
"""检测useEffect依赖问题"""
issues = []
# 检查空依赖数组
if re.search(r'useEffect\s*\([^)]+\)\s*,\s*\[\s*\]', code):
issues.append({
"rule": "empty-deps",
"severity": 15,
"message": "useEffect使用空依赖数组但内部使用了外部变量",
"fix": "添加正确的依赖项"
})
# 检测过多依赖
deps_match = re.search(r'useEffect\s*\([^)]+\)\s*,\s*\[([^\]]+)\]', code)
if deps_match:
deps = [d.strip() for d in deps_match.group(1).split(',')]
if len(deps) > 5:
issues.append({
"rule": "too-many-deps",
"severity": 20,
"message": f"useEffect有{len(deps)}个依赖项,考虑拆分",
"fix": "将复杂useEffect拆分为多个简单的"
})
return issues
def check_inline_props(self, code: str) -> List[Dict]:
"""检测内联props"""
issues = []
# 检测内联对象
if re.search(r'<[A-Z]\w+\s+[^>]*=\{\{', code):
issues.append({
"rule": "inline-object-props",
"severity": 10,
"message": "使用内联对象作为props,会导致子组件重新渲染",
"fix": "使用useMemo缓存对象"
})
# 检测内联函数
if re.search(r'<[A-Z]\w+\s+[^>]*=\{\(\)', code):
issues.append({
"rule": "inline-function-props",
"severity": 10,
"message": "使用内联函数作为props,会导致子组件重新渲染",
"fix": "使用useCallback缓存函数"
})
return issues
def check_missing_keys(self, code: str) -> List[Dict]:
"""检测缺失的key属性"""
issues = []
# 检测.map但没有key
if re.search(r'\.map\s*\([^)]*\)\s*=>\s*\([^)]*\)\s*(<[A-Z])', code):
# 检查是否有key属性
if not re.search(r'\.map.*key=', code):
issues.append({
"rule": "missing-key",
"severity": 25,
"message": "列表渲染缺少key属性",
"fix": "为每个列表项添加唯一的key"
})
return issues
def _generate_suggestions(self, issues: List[Dict]) -> List[str]:
"""生成修复建议"""
suggestions = []
severity_map = {}
for issue in issues:
rule = issue["rule"]
if rule not in severity_map or severity_map[rule] < issue["severity"]:
severity_map[rule] = issue["severity"]
# 按严重度排序
sorted_rules = sorted(severity_map.items(), key=lambda x: x[1], reverse=True)
for rule, severity in sorted_rules:
suggestions.append(f"[优先级{severity}] 修复 {rule}")
return suggestions
# 使用示例
doctor = ReactDoctor()
code = """
function ProductList({ products, filter }) {
const [filtered, setFiltered] = useState([]);
useEffect(() => {
setFiltered(products.filter(p => p.name.includes(filter)));
});
return (
{filtered.map(product => (
handleClick(product)}
product={product}
/>
))}
);
}
"""
result = doctor.diagnose(code)
print(f"代码健康分数: {result['score']}")
print(f"发现问题: {len(result['issues'])}")
for issue in result['issues']:
print(f" - {issue['message']}")
for suggestion in result['suggestions']:
print(f" {suggestion}")