component-refactoring
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseComponent 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 /, or business logic mixed with UI.
useStateuseEffectDify Convention: Place hooks in a subdirectory or alongside the component as .
hooks/use-<feature>.tstypescript
// ❌ 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>
}适用场景:组件包含复杂状态管理、多个/,或业务逻辑与UI代码混合。
useStateuseEffectDify约定:将Hook放置在子目录中,或与组件放在同一目录下,命名为。
hooks/use-<feature>.tstypescript
// ❌ 重构前:组件内包含复杂状态逻辑
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 chains.
if/elsetypescript
// ❌ 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/elsetypescript
// ❌ 重构前:深层嵌套的条件判断
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 for query key organization
NAME_SPACE - Use option for conditional fetching
enabled - Use for data transformation
select - 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
相关技巧
- - For testing refactored components
frontend-testing
- - 用于测试重构后的组件
frontend-testing