React Doctor

世界上有一种医生,专治代码疑难杂症 —— 它不用听诊器,它用AST

🩺 代码体检师 ⚡ 8,668 stars OpenClaw教程 React Doctor教程 Agent工作流

🩺 什么是React Doctor?

React Doctor,就像代码的AI主治医师。你的Agent写的React代码就像实习生写的处方,看起来能跑,但实际上一塌糊涂。React Doctor就是那个把关的老专家,帮你揪出那些让代码"慢性死亡"的问题。

"Agent自信地写了一个300行的useEffect,里面嵌套了5层条件判断。React Doctor看完之后沉默了3秒,然后吐出一句:'这段代码应该被送ICU。'"

React Doctor是一个AI驱动的React代码质量检测工具,它能:

🔍 核心检测能力

1. 性能反模式检测

React Doctor能识别最常见的性能杀手

反模式1: useEffect滥用

在useEffect中做太多事情,导致不必要的重新渲染

修复方案:拆分useEffect

每个useEffect只处理一个副作用,通过依赖数组精确控制

反模式2: 内联对象/函数作为props

每次渲染都创建新对象/函数,导致子组件无意义重新渲染

修复方案:使用useMemo和useCallback

缓存计算结果和回调函数

2. 状态管理诊断

// 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实战应用

1. 集成到Agent工作流

在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)

2. 自动修复

React Doctor不仅能发现问题,还能自动修复

"Agent写了一个满是问题的组件,React Doctor一口气给出了12个诊断结果。最讽刺的是,其中第7条是:'这个组件本身就不应该被创建。'"

💡 实战代码示例

完整诊断流程

# 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}")

🎯 最佳实践

"React Doctor最重要的功能不是告诉你代码有多烂,而是告诉你怎么修好它。就像好医生不只是说'你病了',还要告诉你'吃药,三天后来复查'。"