component-refactoring

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Component Refactoring Skill

组件重构技巧

Refactor high-complexity React components with the patterns and workflow below.
按照以下模式和工作流程重构高复杂度的React组件。

Core Refactoring Patterns

核心重构模式

Pattern 1: Extract Custom Hooks

模式1:提取自定义Hook

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
.
typescript
// ❌ 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>
}
适用场景:组件包含复杂状态管理、多个
useState
/
useEffect
,或业务逻辑与UI代码混合。
Dify约定:将Hook放置在
hooks/
子目录中,或与组件放在同一目录下,命名为
use-<feature>.ts
typescript
// ❌ 重构前:组件内包含复杂状态逻辑
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>
}

Pattern 2: Extract Sub-Components

模式2:提取子组件

When: Single component has multiple UI sections, conditional rendering blocks, or repeated patterns.
typescript
// ❌ 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>
  )
}
适用场景:单个组件包含多个UI区块、条件渲染代码块或重复模式。
typescript
// ❌ 重构前:包含多个区块的单体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>
  )
}

Pattern 3: Simplify Conditional Logic

模式3:简化条件逻辑

When: Deep nesting (> 3 levels), complex ternaries, or multiple
if/else
chains.
typescript
// ❌ 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])
适用场景:深层嵌套(超过3层)、复杂三元表达式或多个
if/else
链式判断。
typescript
// ❌ 重构前:深层嵌套的条件判断
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])

Pattern 4: Extract API/Data Logic

模式4:提取API/数据逻辑

When: Component directly handles API calls, data transformation, or complex async operations.
typescript
// ❌ 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 Best Practices:
  • Define
    NAME_SPACE
    for query key organization
  • Use
    enabled
    option for conditional fetching
  • Use
    select
    for data transformation
  • Export invalidation hooks:
    useInvalidXxx
适用场景:组件直接处理API调用、数据转换或复杂异步操作。
typescript
// ❌ 重构前:API逻辑写在组件内
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])

  // 更多API相关逻辑...
}

// ✅ 重构后:使用React Query提取到数据Hook中
// 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 || {},
  })
}

// 组件变得更简洁
const MCPServiceCard = () => {
  const { data: config, isLoading } = useAppConfig(appId, isBasicApp)
  // 仅保留UI代码
}
React Query最佳实践:
  • 定义
    NAME_SPACE
    用于管理查询键
  • 使用
    enabled
    选项实现条件式数据获取
  • 使用
    select
    进行数据转换
  • 导出失效Hook:
    useInvalidXxx

Pattern 5: Extract Modal/Dialog Management

模式5:提取模态框/对话框管理逻辑

When: Component manages multiple modals with complex open/close states.
typescript
// ❌ 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,
  }
}
适用场景:组件管理多个模态框,且包含复杂的显示/隐藏状态。
typescript
// ❌ 重构前:组件内包含多个模态框状态
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,
  }
}

Common Mistakes to Avoid

需避免的常见错误

❌ Over-Engineering

❌ 过度设计

typescript
// ❌ 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 }
}
typescript
// ❌ 拆分出过多微小的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 }
}

❌ Breaking Existing Patterns

❌ 打破现有模式

  • Follow existing directory structures
  • Maintain naming conventions
  • Preserve export patterns for compatibility
  • 遵循现有的目录结构
  • 保持命名规范
  • 保留兼容的导出模式

❌ Premature Abstraction

❌ 过早抽象

  • Only extract when there's clear complexity benefit
  • Don't create abstractions for single-use code
  • Keep refactored code in the same domain area
  • 仅当明确能降低复杂度时才进行提取
  • 不要为仅使用一次的代码创建抽象
  • 保持重构后的代码在同一业务域内

References

参考资料

Related Skills

相关技巧

  • frontend-testing
    - For testing refactored components
  • frontend-testing
    - 用于测试重构后的组件