mcpField
C
component-refactoring

component-refactoring

github.com/pageai-pro开发33373 次安装17 周前更新
代码仓库

关于 component-refactoring

面向 Dify 代码库开发者的 React 组件重构技能,提供拆分状态、界面、API 逻辑和模态框管理的模式,并强调遵循现有项目约定。

主要功能

  1. 01提取自定义 Hook
  2. 02拆分界面子组件
  3. 03简化条件逻辑
  4. 04分离 API 与数据逻辑
  5. 05集中管理模态框状态

适用场景

  1. 01理清组件中的状态与业务逻辑
  2. 02拆分大型 JSX 界面区块
  3. 03重整组件中的 API 处理

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/pageai-pro/ralph-loop --skill component-refactoring

安装后在 Claude Code 中输入/component-refactoring确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
complexity-patterns.md
component-splitting.md
hook-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