mcpField
C
component-refactoring

component-refactoring

github.com/langflow-ai开发156.3k60 次安装1 天前更新
代码仓库

关于 component-refactoring

为 Langflow 前端 React 组件重构提供流程与规范的开发技能,面向维护复杂组件的开发者。内容涵盖复杂度评估、拆分策略及逐步验证。

主要功能

  1. 01手动评估组件复杂度
  2. 02提取自定义 Hook 与子组件
  3. 03简化条件逻辑和表单处理
  4. 04遵循 Langflow 目录与状态规范
  5. 05逐步运行检查与测试

适用场景

  1. 01拆分超过 300 行的组件
  2. 02整理复杂状态和副作用
  3. 03重构深层嵌套的条件分支

安装

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

$npx skills add https://github.com/langflow-ai/langflow --skill component-refactoring

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

包含的文件

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

references/
complexity-patterns.md
component-splitting.md
hook-extraction.md

详细介绍

Langflow 组件重构技能

使用以下模式和工作流,重构 Langflow 前端代码库中复杂度较高的 React 组件。

复杂度阈值:复杂度 > 50 的组件(通过手动统计条件判断、嵌套层级和代码行数得出)应在测试前重构。

快速参考

命令(在 src/frontend/ 中运行)
cd src/frontend

# Lint with Biome
npm run lint

# Type checking
npm run type-check

# Run tests
npm test
手动评估复杂度

由于 Langflow 没有自动化复杂度分析工具,请手动评估组件:

  1. 统计条件判断:每个 if/else、switch/case、三元表达式及 &&/|| 链均计 +1。
  2. 统计嵌套层级:条件判断或循环中的每个嵌套层级均计 +1。
  3. 统计总行数:每个组件文件以 < 300 行为目标。
  4. 统计状态钩子:超过 5 个 useState 调用,说明可能需要提取钩子。
  5. 统计副作用:超过 3 个 useEffect 调用,说明可能需要合并副作用。
复杂度评分解读
评分级别操作
0-25简单可进行测试
26-50中等考虑小幅重构
51-75复杂测试前重构
76-100非常复杂必须重构

核心重构模式

模式 1:提取自定义钩子

适用情况:组件包含复杂的状态管理、多个 useState/useEffect,或业务逻辑与 UI 混杂。

Langflow 约定:将钩子放在 hooks/ 子目录中,或与组件放在一起并命名为 use-<feature>.ts。Langflow 使用带有 use- 前缀的 kebab-case 文件名。

// Before: Complex state logic in component
const FlowPage: FC = () => {
  const [nodes, setNodes] = useState<Node[]>([])
  const [edges, setEdges] = useState<Edge[]>([])
  const [buildStatus, setBuildStatus] = useState<BuildStatus>(BuildStatus.IDLE)

  // 50+ lines of state management logic...

  return <div>...</div>
}

// After: Extract to custom hook
// hooks/use-flow-state.ts
export const useFlowState = (flowId: string) => {
  const [nodes, setNodes] = useState<Node[]>([])
  const [edges, setEdges] = useState<Edge[]>([])
  const [buildStatus, setBuildStatus] = useState<BuildStatus>(BuildStatus.IDLE)

  // Related state management logic here

  return { nodes, setNodes, edges, setEdges, buildStatus, setBuildStatus }
}

// Component becomes cleaner
const FlowPage: FC = () => {
  const { nodes, setNodes, edges, setEdges } = useFlowState(flowId)
  return <div>...</div>
}

Langflow 示例:

  • src/frontend/src/hooks/use-add-component.ts
  • src/frontend/src/hooks/use-unsaved-changes.ts
  • src/frontend/src/hooks/use-refresh-model-inputs.ts
模式 2:提取子组件

适用情况:单个组件包含多个 UI 区块、条件渲染代码块或重复模式。

Langflow 约定:将子组件放在子目录中,或作为独立文件放在同一目录中。UI 基础组件放在 components/ui/ 中,领域组件放在 components/core/ 中,可复用组件放在 components/common/ 中。

// Before: Monolithic JSX with multiple sections
const GenericNode = () => {
  return (
    <div>
      {/* 100 lines of header UI */}
      {/* 100 lines of parameter fields */}
      {/* 100 lines of output handles */}
    </div>
  )
}

// After: Split into focused components
// CustomNodes/GenericNode/
//   generic-node.tsx       (orchestration only — kebab-case, descriptive name)
//   components/
//     node-header.tsx
//     node-parameters.tsx
//     node-outputs.tsx

const GenericNode = () => {
  return (
    <div>
      <NodeHeader nodeData={data} />
      <NodeParameters fields={fields} />
      <NodeOutputs outputs={outputs} />
    </div>
  )
}

Langflow 示例:

  • src/frontend/src/CustomNodes/GenericNode/components/
  • src/frontend/src/components/core/
  • src/frontend/src/components/ui/
模式 3:简化条件逻辑

适用情况:嵌套过深(> 3 层)、三元表达式复杂,或存在多个 if/else 链。

// Before: Deeply nested conditionals
const getFieldComponent = (field: InputFieldType) => {
  if (field.type === "str") {
    if (field.multiline) {
      return <TextAreaComponent />
    } else if (field.password) {
      return <PasswordInput />
    } else if (field.options?.length) {
      return <Dropdown options={field.options} />
    } else {
      return <InputComponent />
    }
  } else if (field.type === "int") {
    return <IntComponent />
  } else if (field.type === "float") {
    return <FloatComponent />
  }
  return null
}

// After: Use lookup tables + early returns
const FIELD_COMPONENT_MAP: Record<string, FC<FieldProps>> = {
  int: IntComponent,
  float: FloatComponent,
  bool: ToggleComponent,
  code: CodeAreaComponent,
}

const STR_VARIANT_MAP: Record<string, FC<FieldProps>> = {
  multiline: TextAreaComponent,
  password: PasswordInput,
  options: Dropdown,
}

const getFieldComponent = (field: InputFieldType) => {
  if (field.type !== "str") {
    const Component = FIELD_COMPONENT_MAP[field.type]
    return Component ? <Component {...field} /> : null
  }

  const variant = field.multiline ? "multiline"
    : field.password ? "password"
    : field.options?.length ? "options"
    : "default"

  const Component = STR_VARIANT_MAP[variant] ?? InputComponent
  return <Component {...field} />
}
模式 4:提取 API/数据逻辑

适用情况:组件直接处理 API 调用、数据转换或复杂的异步操作。

Langflow 约定:

  • 此技能用于拆分组件,不用于设计查询/变更。
  • 重构数据获取逻辑时,请使用 frontend-query-mutation 处理查询模式、UseRequestProcessor、缓存失效和变更错误处理。
  • 重构时不要创建仅做透传的 useQuery 包装器;只有在自定义钩子确实需要协调多个查询/变更或共享派生状态时,才提取该钩子。
  • API 钩子位于 controllers/API/queries/{domain}/ 中。

Langflow 示例:

  • src/frontend/src/controllers/API/queries/flows/use-post-add-flow.ts
  • src/frontend/src/controllers/API/queries/variables/use-get-global-variables.ts
  • src/frontend/src/controllers/API/queries/folders/use-get-folders.ts
模式 5:提取模态框/对话框管理逻辑

适用情况:组件管理多个模态框,且打开/关闭状态较为复杂。

Langflow 约定:应将模态框及其状态管理一并提取。

// Before: Multiple modal states in component
const FlowToolbar = () => {
  const [showExportModal, setShowExportModal] = useState(false)
  const [showShareModal, setShowShareModal] = useState(false)
  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)
  const [showApiModal, setShowApiModal] = useState(false)
  // 5+ more modal states...
}

// After: Extract to modal management hook
type ModalType = "export" | "share" | "delete" | "api" | null

const useFlowToolbarModals = () => {
  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,
  }
}
模式 6:提取表单逻辑

适用情况:表单验证、提交处理或字段转换较为复杂。

Langflow 约定:将表单状态和验证逻辑提取到钩子中。

// Extract form validation and submission
const useFlowSettingsForm = (initialValues: FlowSettings) => {
  const [values, setValues] = useState(initialValues)
  const [errors, setErrors] = useState<Record<string, string>>({})

  const validate = useCallback(() => {
    const newErrors: Record<string, string> = {}
    if (!values.name?.trim()) newErrors.name = "Name is required"
    if (values.endpoint_name && !/^[a-z0-9-]+$/.test(values.endpoint_name)) {
      newErrors.endpoint_name = "Must be lowercase alphanumeric with hyphens"
    }
    setErrors(newErrors)
    return Object.keys(newErrors).length === 0
  }, [values])

  const handleChange = useCallback((field: string, value: any) => {
    setValues((prev) => ({ ...prev, [field]: value }))
  }, [])

  return { values, errors, validate, handleChange }
}

Langflow 特定的重构指南

1. Zustand Store 选择器

适用场景:组件从 Zustand Store 中读取多个值,导致不必要的重新渲染。

// Before: Selecting too many values
const Component = () => {
  const flowStore = useFlowStore()
  // Component re-renders on ANY store change
}

// After: Use individual selectors
const Component = () => {
  const nodes = useFlowStore((state) => state.nodes)
  const edges = useFlowStore((state) => state.edges)
  // Component only re-renders when nodes or edges change
}

Langflow 参考:src/frontend/src/stores/ 中的所有 Store 都遵循此选择器模式。

2. 自定义节点组件

适用场景:重构流程节点组件(CustomNodes/GenericNode/)。

约定:

  • 将节点逻辑放在自定义 Hook 中
  • 将参数渲染提取到独立组件中
  • 使用现有的 components/ 子目录存放子组件
CustomNodes/GenericNode/
  generic-node.tsx             # Node registration and main render (kebab-case, NOT index.tsx)
  components/
    handle-render.tsx           # Handle rendering
    node-description.tsx        # Node description display
    node-input-field.tsx        # Input field rendering
    node-name.tsx               # Node name display
    node-output-field.tsx       # Output field rendering
    node-status.tsx             # Build status display
3. 流程画布组件

适用场景:重构与流程编辑器画布相关的组件。

约定:

  • @xyflow/react v12 是画布库
  • 将画布事件处理程序与 UI 渲染分开
  • 使用 useFlowStore 管理流程状态
  • 使用 useFlowsManagerStore 管理多个流程
4. API 查询 Hook 组件

适用场景:重构使用 API 数据的组件。

约定:

  • 使用 controllers/API/queries/ 中现有的查询 Hook
  • 通过 UseRequestProcessor 处理新的查询/变更
  • 使用 ["useGetGlobalVariables"] 这类查询键数组来管理缓存
  • 缓存失效应放在变更的 onSettled 回调中

重构工作流

步骤 1:评估复杂度

手动统计:

  • 条件表达式总数(if/else、switch、三元表达式、&&/||)
  • 最大嵌套深度
  • 代码总行数
  • useState/useEffect Hook 数量
  • 不同 UI 区块的数量
步骤 2:制定计划

根据检测到的特征制定重构计划:

检测到的特征重构操作
有 5 个或更多 useState Hook,且状态彼此相关提取自定义 Hook
组件主体中包含 API 调用提取为查询 Hook
有 3 个或更多包含逻辑的事件处理程序将事件处理程序提取到 Hook 中
代码超过 300 行拆分为子组件
条件嵌套较深(>3)简化条件逻辑
有多个模态框状态提取模态框管理逻辑
步骤 3:逐步执行
  1. 一次提取一部分
  2. 每次提取后运行 lint、类型检查和测试
  3. 验证功能后再进行下一步
For each extraction:
  1. Extract code
  2. Run: npm run lint
  3. Run: npm run type-check
  4. Run: npm test
  5. Test functionality manually
  6. PASS? -> Next extraction
     FAIL? -> Fix before continuing
步骤 4:验证

重构后,手动重新评估复杂度:

  • 目标复杂度 < 50
  • 目标行数 < 300
  • 目标最大嵌套深度 <= 3
  • 目标最大函数长度 < 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 }
}
破坏现有模式
  • 遵循 components/ui/、components/core/、components/common/ 中现有的目录结构
  • 遵循命名约定(文件使用 kebab-case,组件使用 PascalCase)
  • 保留导出模式以确保兼容性
  • 保持 Zustand Store 选择器模式一致
过早抽象
  • 仅在明确有助于降低复杂度时才进行提取
  • 不要为仅使用一次的代码创建抽象
  • 将重构后的代码保留在同一领域中
绕过 UseRequestProcessor
  • 不要直接调用 useQuery 或 useMutation 来发起 API 调用
  • 始终使用 UseRequestProcessor 模式,以确保重试和失效逻辑的一致性
  • API Hook 模式请参阅 frontend-query-mutation 技能

参考资料

Langflow 代码库示例
  • Hook 提取:src/frontend/src/hooks/
  • 组件拆分:src/frontend/src/CustomNodes/GenericNode/components/
  • UI 组件:src/frontend/src/components/ui/
  • 核心组件:src/frontend/src/components/core/
  • 通用组件:src/frontend/src/components/common/
  • API 查询 Hook:src/frontend/src/controllers/API/queries/
  • Zustand Store:src/frontend/src/stores/
相关技能
  • frontend-query-mutation - API 查询和变更模式
  • frontend-testing - 测试重构后的组件

评论

支持 Markdown