You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React多步骤配置表单:父子步骤显示与状态管理方案问询

React多步骤配置表单核心需求实现方案

核心状态设计

  • selectedOptions: 记录各层级选中的选项,键为步骤ID,值为选中的选项值
  • expandedSteps: 用Set存储所有展开的步骤ID,便于快速增删和判断展开状态

逐个实现核心需求

1. 选中关联选项时自动展开子步骤及下层

首先给步骤JSON的选项添加childrenStepId字段,标记该选项对应的子步骤:

{
  "steps": [
    {
      "id": "top_step",
      "options": [
        { "value": "opt1", "label": "选项1", "childrenStepId": "sub_step1" },
        { "value": "opt2", "label": "选项2" }
      ]
    },
    {
      "id": "sub_step1",
      "parentId": "top_step",
      "options": [
        { "value": "sub_opt1", "label": "子选项1", "childrenStepId": "sub_sub_step1" }
      ]
    }
  ]
}

实现递归展开函数:

const expandStepAndChildren = (stepId) => {
  const step = steps.find(s => s.id === stepId);
  if (!step) return;
  setExpandedSteps(prev => new Set([...prev, stepId]));
  // 递归展开所有子步骤
  step.options.forEach(opt => {
    if (opt.childrenStepId) {
      expandStepAndChildren(opt.childrenStepId);
    }
  });
};

// 下拉选项变化时触发
const handleOptionChange = (stepId, value) => {
  setSelectedOptions(prev => ({ ...prev, [stepId]: value }));
  const selectedOpt = steps.find(s => s.id === stepId)?.options.find(o => o.value === value);
  if (selectedOpt?.childrenStepId) {
    expandStepAndChildren(selectedOpt.childrenStepId);
  }
};

2. 切换顶层选项时折叠所有对应子步骤

实现递归折叠函数:

const collapseAllChildren = (stepId) => {
  const step = steps.find(s => s.id === stepId);
  if (!step) return;
  // 移除当前步骤的展开状态
  setExpandedSteps(prev => {
    const newSet = new Set(prev);
    newSet.delete(stepId);
    return newSet;
  });
  // 清空当前步骤的选中状态
  setSelectedOptions(prev => {
    const newOpts = { ...prev };
    delete newOpts[stepId];
    return newOpts;
  });
  // 递归处理子步骤
  const childSteps = steps.filter(s => s.parentId === stepId);
  childSteps.forEach(child => collapseAllChildren(child.id));
};

// 顶层选项变化时触发
const handleTopOptionChange = (value) => {
  // 折叠之前顶层选项对应的所有子步骤
  const prevTopValue = selectedOptions.top_step;
  if (prevTopValue) {
    const prevOpt = steps.find(s => s.id === 'top_step')?.options.find(o => o.value === prevTopValue);
    if (prevOpt?.childrenStepId) {
      collapseAllChildren(prevOpt.childrenStepId);
    }
  }
  // 更新顶层选中状态
  setSelectedOptions(prev => ({ ...prev, top_step: value }));
  // 展开新选项对应的子步骤(如果有)
  const newOpt = steps.find(s => s.id === 'top_step')?.options.find(o => o.value === value);
  if (newOpt?.childrenStepId) {
    expandStepAndChildren(newOpt.childrenStepId);
  }
};

3. 生成仅包含有效字段的配置

实现递归收集有效配置的函数:

const getValidConfig = () => {
  const config = {};
  const processStep = (stepId) => {
    const step = steps.find(s => s.id === stepId);
    if (!step || !expandedSteps.has(stepId)) return;
    const selectedValue = selectedOptions[stepId];
    if (selectedValue) {
      config[stepId] = selectedValue;
      // 递归处理关联的子步骤
      const selectedOpt = step.options.find(o => o.value === selectedValue);
      if (selectedOpt?.childrenStepId) {
        processStep(selectedOpt.childrenStepId);
      }
    }
  };
  // 从所有顶层步骤开始遍历处理
  steps.filter(s => !s.parentId).forEach(topStep => processStep(topStep.id));
  return config;
};

// 提交时调用
const handleSubmit = () => {
  const validConfig = getValidConfig();
  // 提交到服务器
  console.log('提交配置:', validConfig);
};

渲染逻辑示例

递归渲染步骤组件:

const StepComponent = ({ stepId }) => {
  const step = steps.find(s => s.id === stepId);
  if (!step) return null;
  const isExpanded = expandedSteps.has(stepId);
  const selectedValue = selectedOptions[stepId];

  return (
    <div className={`step ${isExpanded ? 'expanded' : ''}`}>
      <select
        value={selectedValue || ''}
        onChange={(e) => handleOptionChange(stepId, e.target.value)}
      >
        <option value="">请选择</option>
        {step.options.map(opt => (
          <option key={opt.value} value={opt.value}>{opt.label}</option>
        ))}
      </select>
      {/* 渲染关联子步骤(仅当当前步骤展开且有选中选项时) */}
      {isExpanded && selectedValue && (
        <>
          {step.options.find(o => o.value === selectedValue)?.childrenStepId && (
            <StepComponent stepId={step.options.find(o => o.value === selectedValue).childrenStepId} />
          )}
        </>
      )}
    </div>
  );
};

// 主组件中渲染所有顶层步骤
return (
  <div className="configurator">
    {steps.filter(s => !s.parentId).map(topStep => (
      <StepComponent key={topStep.id} stepId={topStep.id} />
    ))}
    <button onClick={handleSubmit}>提交配置</button>
  </div>
);

内容的提问来源于stack exchange,提问作者Robbert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 02:45:36