关于 component-refactoring
面向 Dify 代码库开发者的 React 组件重构技能,提供拆分状态、界面、API 逻辑和模态框管理的模式,并强调遵循现有项目约定。
主要功能
- 01提取自定义 Hook
- 02拆分界面子组件
- 03简化条件逻辑
- 04分离 API 与数据逻辑
- 05集中管理模态框状态
适用场景
- 01理清组件中的状态与业务逻辑
- 02拆分大型 JSX 界面区块
- 03重整组件中的 API 处理
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ pageai-pro/ ralph-loop --skill component-refactoring安装后在 Claude Code 中输入/component-refactoring确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/complexity-patterns.mdcomponent-splitting.mdhook-extraction.md详细介绍
组件重构技能
使用以下模式和工作流重构复杂度较高的 React 组件。
核心重构模式
模式 1:提取自定义 Hook
适用情形:组件的状态管理复杂、包含多个 useState/useEffect,或业务逻辑与 UI 混杂。
Dify 约定:将 Hook 放在 hooks/ 子目录中,或与组件放在一起,并命名为 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>
}
模式 2:提取子组件
适用情形:单个组件包含多个 UI 区块、条件渲染代码块或重复模式。
// ❌ 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>
)
}
模式 3:简化条件逻辑
适用情形:嵌套层级过深(> 3 层)、三元表达式复杂,或包含多组 if/else 链。
// ❌ 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])
模式 4:提取 API/数据逻辑
适用情形:组件直接处理 API 调用、数据转换或复杂的异步操作。
// ❌ Before: API logic in component
const MCPServiceCard = () => {
const [basicAppConfig, setBasicAppConfig] = useState({})
useEffect(() => {
if (isBasicApp && appId) {
(async () => {
const res = await fetchAppDetail({ url: '/apps', id: appId })
setBasicAppConfig(res?.model_config || {})
})()
}
}, [appId, isBasicApp])
// More API-related logic...
}
// ✅ After: Extract to data hook using React Query
// use-app-config.ts
import { useQuery } from '@tanstack/react-query'
import { get } from '@/service/base'
const NAME_SPACE = 'appConfig'
export const useAppConfig = (appId: string, isBasicApp: boolean) => {
return useQuery({
enabled: isBasicApp && !!appId,
queryKey: [NAME_SPACE, 'detail', appId],
queryFn: () => get<AppDetailResponse>(`/apps/${appId}`),
select: data => data?.model_config || {},
})
}
// Component becomes cleaner
const MCPServiceCard = () => {
const { data: config, isLoading } = useAppConfig(appId, isBasicApp)
// UI only
}
React Query 最佳实践:
- 定义
NAME_SPACE,用于组织查询键 - 使用
enabled选项进行条件获取 - 使用
select转换数据 - 导出失效 Hook:
useInvalidXxx
模式 5:提取模态框/对话框管理逻辑
适用情形:组件管理多个模态框,且打开/关闭状态复杂。
// ❌ 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,
}
}
应避免的常见错误
❌ 过度设计
// ❌ 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 }
}
❌ 破坏现有模式
- 遵循现有目录结构
- 保持命名约定
- 保留导出模式以确保兼容性
❌ 过早抽象
- 仅在明确能降低复杂度时才提取
- 不要为只使用一次的代码创建抽象
- 将重构后的代码保留在相同的领域范围内
参考
相关技能
frontend-testing- 用于测试重构后的组件
评论
支持 Markdown