component-refactoring by langgenius/dify
npx skills add https://github.com/langgenius/dify --skill component-refactoring使用以下模式和工作流程重构 Dify 前端代码库中高复杂度的 React 组件。
复杂度阈值:复杂度 > 50(通过
pnpm analyze-component测量)的组件应在测试前进行重构。
web/ 目录运行)使用相对于 web/ 的路径(例如,app/components/...)。使用 refactor-component 生成重构提示,使用 analyze-component 生成测试提示和指标。
cd web
# 生成重构提示
pnpm refactor-component <路径>
# 以 JSON 格式输出重构分析
pnpm refactor-component <路径> --json
# 生成测试提示(重构后)
pnpm analyze-component <路径>
# 以 JSON 格式输出测试分析
pnpm analyze-component <路径> --json
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
# 分析组件复杂度
pnpm analyze-component <路径> --json
# 需要检查的关键指标:
# - complexity: 标准化分数 0-100(目标 < 50)
# - maxComplexity: 单个函数最高复杂度
# - lineCount: 总行数(目标 < 300)
| 分数 | 等级 | 操作 |
|---|---|---|
| 0-25 | 🟢 简单 | 准备测试 |
| 26-50 | 🟡 中等 | 考虑小幅重构 |
| 51-75 | 🟠 复杂 | 测试前必须重构 |
| 76-100 | 🔴 非常复杂 | 必须重构 |
适用场景:组件具有复杂的状态管理、多个 useState/useEffect,或业务逻辑与 UI 混杂。
Dify 约定:将 Hook 放在 hooks/ 子目录中,或与组件放在一起,命名为 use-<功能>.ts。
// ❌ 重构前:组件中包含复杂的状态逻辑
const Configuration: FC = () => {
const [modelConfig, setModelConfig] = useState<ModelConfig>(...)
const [datasetConfigs, setDatasetConfigs] = useState<DatasetConfigs>(...)
const [completionParams, setCompletionParams] = useState<FormValue>({})
// 50+ 行的状态管理逻辑...
return <div>...</div>
}
// ✅ 重构后:提取到自定义 Hook
// hooks/use-model-config.ts
export const useModelConfig = (appId: string) => {
const [modelConfig, setModelConfig] = useState<ModelConfig>(...)
const [completionParams, setCompletionParams] = useState<FormValue>({})
// 相关的状态管理逻辑放在这里
return { modelConfig, setModelConfig, completionParams, setCompletionParams }
}
// 组件变得更简洁
const Configuration: FC = () => {
const { modelConfig, setModelConfig } = useModelConfig(appId)
return <div>...</div>
}
Dify 示例:
web/app/components/app/configuration/hooks/use-advanced-prompt-config.tsweb/app/components/app/configuration/debug/hooks.tsxweb/app/components/workflow/hooks/use-workflow.ts适用场景:单个组件包含多个 UI 部分、条件渲染块或重复模式。
Dify 约定:将子组件放在子目录中,或作为同一目录下的单独文件。
// ❌ 重构前:包含多个部分的庞大 JSX
const AppInfo = () => {
return (
<div>
{/* 100 行头部 UI */}
{/* 100 行操作 UI */}
{/* 100 行模态框 */}
</div>
)
}
// ✅ 重构后:拆分为专注的组件
// app-info/
// ├── index.tsx (仅负责编排)
// ├── app-header.tsx (头部 UI)
// ├── app-operations.tsx (操作 UI)
// └── app-modals.tsx (模态框管理)
const AppInfo = () => {
const { showModal, setShowModal } = useAppInfoModals()
return (
<div>
<AppHeader appDetail={appDetail} />
<AppOperations onAction={handleAction} />
<AppModals show={showModal} onClose={() => setShowModal(null)} />
</div>
)
}
Dify 示例:
web/app/components/app/configuration/ 目录结构web/app/components/workflow/nodes/ 按节点组织的结构适用场景:深度嵌套(> 3 层)、复杂的三元运算符或多个 if/else 链。
// ❌ 重构前:深度嵌套的条件语句
const Template = useMemo(() => {
if (appDetail?.mode === AppModeEnum.CHAT) {
switch (locale) {
case LanguagesSupported[1]:
return <TemplateChatZh />
case LanguagesSupported[7]:
return <TemplateChatJa />
default:
return <TemplateChatEn />
}
}
if (appDetail?.mode === AppModeEnum.ADVANCED_CHAT) {
// 另外 15 行...
}
// 更多条件...
}, [appDetail, locale])
// ✅ 重构后:使用查找表 + 提前返回
const TEMPLATE_MAP = {
[AppModeEnum.CHAT]: {
[LanguagesSupported[1]]: TemplateChatZh,
[LanguagesSupported[7]]: TemplateChatJa,
default: TemplateChatEn,
},
[AppModeEnum.ADVANCED_CHAT]: {
[LanguagesSupported[1]]: TemplateAdvancedChatZh,
// ...
},
}
const Template = useMemo(() => {
const modeTemplates = TEMPLATE_MAP[appDetail?.mode]
if (!modeTemplates) return null
const TemplateComponent = modeTemplates[locale] || modeTemplates.default
return <TemplateComponent appDetail={appDetail} />
}, [appDetail, locale])
适用场景:组件直接处理 API 调用、数据转换或复杂的异步操作。
Dify 约定:
web/AGENTS.md。frontend-query-mutation。useInvalid / useReset。useQuery 包装器;仅当自定义 Hook 真正编排多个查询/突变或共享派生状态时才提取它。Dify 示例:
web/service/use-workflow.tsweb/service/use-common.tsweb/service/knowledge/use-dataset.tsweb/service/knowledge/use-document.ts适用场景:组件管理多个具有复杂打开/关闭状态的模态框。
Dify 约定:模态框应与其状态管理一起提取。
// ❌ 重构前:组件中有多个模态框状态
const AppInfo = () => {
const [showEditModal, setShowEditModal] = useState(false)
const [showDuplicateModal, setShowDuplicateModal] = useState(false)
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
const [showSwitchModal, setShowSwitchModal] = useState(false)
const [showImportDSLModal, setShowImportDSLModal] = useState(false)
// 5+ 个模态框状态...
}
// ✅ 重构后:提取到模态框管理 Hook
type ModalType = 'edit' | 'duplicate' | 'delete' | 'switch' | 'import' | null
const useAppInfoModals = () => {
const [activeModal, setActiveModal] = useState<ModalType>(null)
const openModal = useCallback((type: ModalType) => setActiveModal(type), [])
const closeModal = useCallback(() => setActiveModal(null), [])
return {
activeModal,
openModal,
closeModal,
isOpen: (type: ModalType) => activeModal === type,
}
}
适用场景:复杂的表单验证、提交处理或字段转换。
Dify 约定:使用 web/app/components/base/form/ 中的 @tanstack/react-form 模式。
// ✅ 使用现有的表单基础设施
import { useAppForm } from '@/app/components/base/form'
const ConfigForm = () => {
const form = useAppForm({
defaultValues: { name: '', description: '' },
onSubmit: handleSubmit,
})
return <form.Provider>...</form.Provider>
}
适用场景:组件提供具有多个状态的复杂上下文值。
// ❌ 重构前:庞大的上下文值对象
const value = {
appId, isAPIKeySet, isTrailFinished, mode, modelModeType,
promptMode, isAdvancedMode, isAgent, isOpenAI, isFunctionCall,
// 50+ 个属性...
}
return <ConfigContext.Provider value={value}>...</ConfigContext.Provider>
// ✅ 重构后:拆分为特定领域的上下文
<ModelConfigProvider value={modelConfigValue}>
<DatasetConfigProvider value={datasetConfigValue}>
<UIConfigProvider value={uiConfigValue}>
{children}
</UIConfigProvider>
</DatasetConfigProvider>
</ModelConfigProvider>
Dify 参考:web/context/ 目录结构
适用场景:重构工作流节点组件(web/app/components/workflow/nodes/)。
约定:
将节点逻辑保留在 use-interactions.ts 中
将面板 UI 提取到单独的文件
使用 _base 组件处理通用模式
nodes/<节点类型>/ ├── index.tsx # 节点注册 ├── node.tsx # 节点视觉组件 ├── panel.tsx # 配置面板 ├── use-interactions.ts # 节点特定的 Hook └── types.ts # 类型定义
适用场景:重构应用配置组件。
约定:
web/app/components/app/configuration/ 中的现有模式适用场景:重构与工具相关的组件(web/app/components/tools/)。
约定:
web/service/use-tools.ts 中的服务 Hookpnpm refactor-component <路径>
此命令将:
pnpm analyze-component <路径> --json
识别:
根据检测到的功能创建重构计划:
| 检测到的功能 | 重构操作 |
|---|---|
hasState: true + hasEffects: true | 提取自定义 Hook |
hasAPI: true | 提取数据/服务 Hook |
hasEvents: true (多个) | 提取事件处理器 |
lineCount > 300 | 拆分为子组件 |
maxComplexity > 50 | 简化条件逻辑 |
对于每次提取:
┌────────────────────────────────────────┐
│ 1. 提取代码 │
│ 2. 运行: pnpm lint:fix │
│ 3. 运行: pnpm type-check:tsgo │
│ 4. 运行: pnpm test │
│ 5. 手动测试功能 │
│ 6. 通过? → 进行下一次提取 │
│ 失败? → 修复后再继续 │
└────────────────────────────────────────┘
重构后:
# 重新运行重构命令以验证改进
pnpm refactor-component <路径>
# 如果复杂度 < 25 且行数 < 200,你将看到:
# ✅ 组件结构良好
# 获取详细指标:
pnpm analyze-component <路径> --json
# 目标指标:
# - complexity < 50
# - lineCount < 300
# - maxComplexity < 30
// ❌ 太多微小的 Hook
const useButtonText = () => useState('Click')
const useButtonDisabled = () => useState(false)
const useButtonLoading = () => useState(false)
// ✅ 包含相关状态的聚合 Hook
const useButtonState = () => {
const [text, setText] = useState('Click')
const [disabled, setDisabled] = useState(false)
const [loading, setLoading] = useState(false)
return { text, setText, disabled, setDisabled, loading, setLoading }
}
web/app/components/app/configuration/hooks/web/app/components/app/configuration/web/service/use-*.tsweb/app/components/workflow/hooks/web/app/components/base/form/frontend-testing - 用于测试重构后的组件web/docs/test.md - 测试规范每周安装量
2.8K
代码仓库
GitHub 星标数
134.2K
首次出现
2026年1月20日
安全审计
安装于
gemini-cli2.1K
opencode2.1K
codex2.0K
claude-code1.9K
github-copilot1.8K
amp1.6K
Refactor high-complexity React components in the Dify frontend codebase with the patterns and workflow below.
Complexity Threshold : Components with complexity > 50 (measured by
pnpm analyze-component) should be refactored before testing.
web/)Use paths relative to web/ (e.g., app/components/...). Use refactor-component for refactoring prompts and analyze-component for testing prompts and metrics.
cd web
# Generate refactoring prompt
pnpm refactor-component <path>
# Output refactoring analysis as JSON
pnpm refactor-component <path> --json
# Generate testing prompt (after refactoring)
pnpm analyze-component <path>
# Output testing analysis as JSON
pnpm analyze-component <path> --json
# Analyze component complexity
pnpm analyze-component <path> --json
# Key metrics to check:
# - complexity: normalized score 0-100 (target < 50)
# - maxComplexity: highest single function complexity
# - lineCount: total lines (target < 300)
| Score | Level | Action |
|---|---|---|
| 0-25 | 🟢 Simple | Ready for testing |
| 26-50 | 🟡 Medium | Consider minor refactoring |
| 51-75 | 🟠 Complex | Refactor before testing |
| 76-100 | 🔴 Very Complex | Must refactor |
When : Component has complex state management, multiple useState/useEffect, or business logic mixed with UI.
Dify Convention : Place hooks in a hooks/ subdirectory or alongside the component as use-<feature>.ts.
// ❌ Before: Complex state logic in component
const Configuration: FC = () => {
const [modelConfig, setModelConfig] = useState<ModelConfig>(...)
const [datasetConfigs, setDatasetConfigs] = useState<DatasetConfigs>(...)
const [completionParams, setCompletionParams] = useState<FormValue>({})
// 50+ lines of state management logic...
return <div>...</div>
}
// ✅ After: Extract to custom hook
// hooks/use-model-config.ts
export const useModelConfig = (appId: string) => {
const [modelConfig, setModelConfig] = useState<ModelConfig>(...)
const [completionParams, setCompletionParams] = useState<FormValue>({})
// Related state management logic here
return { modelConfig, setModelConfig, completionParams, setCompletionParams }
}
// Component becomes cleaner
const Configuration: FC = () => {
const { modelConfig, setModelConfig } = useModelConfig(appId)
return <div>...</div>
}
Dify Examples :
web/app/components/app/configuration/hooks/use-advanced-prompt-config.tsweb/app/components/app/configuration/debug/hooks.tsxweb/app/components/workflow/hooks/use-workflow.tsWhen : Single component has multiple UI sections, conditional rendering blocks, or repeated patterns.
Dify Convention : Place sub-components in subdirectories or as separate files in the same directory.
// ❌ Before: Monolithic JSX with multiple sections
const AppInfo = () => {
return (
<div>
{/* 100 lines of header UI */}
{/* 100 lines of operations UI */}
{/* 100 lines of modals */}
</div>
)
}
// ✅ After: Split into focused components
// app-info/
// ├── index.tsx (orchestration only)
// ├── app-header.tsx (header UI)
// ├── app-operations.tsx (operations UI)
// └── app-modals.tsx (modal management)
const AppInfo = () => {
const { showModal, setShowModal } = useAppInfoModals()
return (
<div>
<AppHeader appDetail={appDetail} />
<AppOperations onAction={handleAction} />
<AppModals show={showModal} onClose={() => setShowModal(null)} />
</div>
)
}
Dify Examples :
web/app/components/app/configuration/ directory structureweb/app/components/workflow/nodes/ per-node organizationWhen : Deep nesting (> 3 levels), complex ternaries, or multiple if/else chains.
// ❌ Before: Deeply nested conditionals
const Template = useMemo(() => {
if (appDetail?.mode === AppModeEnum.CHAT) {
switch (locale) {
case LanguagesSupported[1]:
return <TemplateChatZh />
case LanguagesSupported[7]:
return <TemplateChatJa />
default:
return <TemplateChatEn />
}
}
if (appDetail?.mode === AppModeEnum.ADVANCED_CHAT) {
// Another 15 lines...
}
// More conditions...
}, [appDetail, locale])
// ✅ After: Use lookup tables + early returns
const TEMPLATE_MAP = {
[AppModeEnum.CHAT]: {
[LanguagesSupported[1]]: TemplateChatZh,
[LanguagesSupported[7]]: TemplateChatJa,
default: TemplateChatEn,
},
[AppModeEnum.ADVANCED_CHAT]: {
[LanguagesSupported[1]]: TemplateAdvancedChatZh,
// ...
},
}
const Template = useMemo(() => {
const modeTemplates = TEMPLATE_MAP[appDetail?.mode]
if (!modeTemplates) return null
const TemplateComponent = modeTemplates[locale] || modeTemplates.default
return <TemplateComponent appDetail={appDetail} />
}, [appDetail, locale])
When : Component directly handles API calls, data transformation, or complex async operations.
Dify Convention :
web/AGENTS.md.frontend-query-mutation for contracts, query shape, data-fetching wrappers, query/mutation call-site patterns, conditional queries, invalidation, and mutation error handling.useInvalid / useReset.useQuery wrappers during refactoring; only extract a custom hook when it truly orchestrates multiple queries/mutations or shared derived state.Dify Examples :
web/service/use-workflow.tsweb/service/use-common.tsweb/service/knowledge/use-dataset.tsweb/service/knowledge/use-document.tsWhen : Component manages multiple modals with complex open/close states.
Dify Convention : Modals should be extracted with their state management.
// ❌ Before: Multiple modal states in component
const AppInfo = () => {
const [showEditModal, setShowEditModal] = useState(false)
const [showDuplicateModal, setShowDuplicateModal] = useState(false)
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
const [showSwitchModal, setShowSwitchModal] = useState(false)
const [showImportDSLModal, setShowImportDSLModal] = useState(false)
// 5+ more modal states...
}
// ✅ After: Extract to modal management hook
type ModalType = 'edit' | 'duplicate' | 'delete' | 'switch' | 'import' | null
const useAppInfoModals = () => {
const [activeModal, setActiveModal] = useState<ModalType>(null)
const openModal = useCallback((type: ModalType) => setActiveModal(type), [])
const closeModal = useCallback(() => setActiveModal(null), [])
return {
activeModal,
openModal,
closeModal,
isOpen: (type: ModalType) => activeModal === type,
}
}
When : Complex form validation, submission handling, or field transformation.
Dify Convention : Use @tanstack/react-form patterns from web/app/components/base/form/.
// ✅ Use existing form infrastructure
import { useAppForm } from '@/app/components/base/form'
const ConfigForm = () => {
const form = useAppForm({
defaultValues: { name: '', description: '' },
onSubmit: handleSubmit,
})
return <form.Provider>...</form.Provider>
}
When : Component provides complex context values with multiple states.
// ❌ Before: Large context value object
const value = {
appId, isAPIKeySet, isTrailFinished, mode, modelModeType,
promptMode, isAdvancedMode, isAgent, isOpenAI, isFunctionCall,
// 50+ more properties...
}
return <ConfigContext.Provider value={value}>...</ConfigContext.Provider>
// ✅ After: Split into domain-specific contexts
<ModelConfigProvider value={modelConfigValue}>
<DatasetConfigProvider value={datasetConfigValue}>
<UIConfigProvider value={uiConfigValue}>
{children}
</UIConfigProvider>
</DatasetConfigProvider>
</ModelConfigProvider>
Dify Reference : web/context/ directory structure
When : Refactoring workflow node components (web/app/components/workflow/nodes/).
Conventions :
Keep node logic in use-interactions.ts
Extract panel UI to separate files
Use _base components for common patterns
nodes/<node-type>/ ├── index.tsx # Node registration ├── node.tsx # Node visual component ├── panel.tsx # Configuration panel ├── use-interactions.ts # Node-specific hooks └── types.ts # Type definitions
When : Refactoring app configuration components.
Conventions :
web/app/components/app/configuration/When : Refactoring tool-related components (web/app/components/tools/).
Conventions :
web/service/use-tools.tspnpm refactor-component <path>
This command will:
pnpm analyze-component <path> --json
Identify:
Create a refactoring plan based on detected features:
| Detected Feature | Refactoring Action |
|---|---|
hasState: true + hasEffects: true | Extract custom hook |
hasAPI: true | Extract data/service hook |
hasEvents: true (many) | Extract event handlers |
lineCount > 300 | Split into sub-components |
maxComplexity > 50 | Simplify conditional logic |
For each extraction:
┌────────────────────────────────────────┐
│ 1. Extract code │
│ 2. Run: pnpm lint:fix │
│ 3. Run: pnpm type-check:tsgo │
│ 4. Run: pnpm test │
│ 5. Test functionality manually │
│ 6. PASS? → Next extraction │
│ FAIL? → Fix before continuing │
└────────────────────────────────────────┘
After refactoring:
# Re-run refactor command to verify improvements
pnpm refactor-component <path>
# If complexity < 25 and lines < 200, you'll see:
# ✅ COMPONENT IS WELL-STRUCTURED
# For detailed metrics:
pnpm analyze-component <path> --json
# Target metrics:
# - complexity < 50
# - lineCount < 300
# - maxComplexity < 30
// ❌ Too many tiny hooks
const useButtonText = () => useState('Click')
const useButtonDisabled = () => useState(false)
const useButtonLoading = () => useState(false)
// ✅ Cohesive hook with related state
const useButtonState = () => {
const [text, setText] = useState('Click')
const [disabled, setDisabled] = useState(false)
const [loading, setLoading] = useState(false)
return { text, setText, disabled, setDisabled, loading, setLoading }
}
web/app/components/app/configuration/hooks/web/app/components/app/configuration/web/service/use-*.tsweb/app/components/workflow/hooks/web/app/components/base/form/frontend-testing - For testing refactored componentsweb/docs/test.md - Testing specificationWeekly Installs
2.8K
Repository
GitHub Stars
134.2K
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli2.1K
opencode2.1K
codex2.0K
claude-code1.9K
github-copilot1.8K
amp1.6K
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装