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
相关产品推荐
相关产品推荐

