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

React嵌套动态表单新增父表单项时子表单自动加载如何解决

问题根因

你当前的两级嵌套表单共用了全局的bookvideoc状态存储所有子视频项,所有渲染出来的主视频表单项都会读取同一个子视频列表渲染,所以新增主视频项时,自然会把当前全局存储的子视频全部显示出来。另外你代码中还有输入框值绑定错误的问题,所有主项的输入会共享同一个值。

修复步骤

1. 调整主视频模板结构

给每个主视频项新增独立的children字段存储自己的子视频列表,避免全局共享:

const videoTemplet = { 
  book_All_details: "", 
  number_of_chapters:"", 
  chapter:'', 
  video_title: "", 
  description: "", 
  video_url: "", 
  video_image: "",
  // 新增子视频专属字段,每个主项独立持有子列表
  children: [] 
}
const [bookvideo ,setBookvideo] = useState([videoTemplet]);

2. 删除全局子视频相关状态,替换为父子关联的操作方法

删除原来全局定义的bookvideoc、addVideoc、onChangec、removeFormFieldsc,新增以下支持父级索引定位的操作方法:

// 新增子视频:指定对应主视频的索引
const addChildVideo = (parentIndex) => {
  const childTemplet = { 
    book_All_details: "", number_of_chapters:"", chapter:'', 
    video_title: "", description: "", video_url: "", video_image: ""
  }
  const updatedList = bookvideo.map((item, i) => {
    if(i === parentIndex) {
      return {...item, children: [...item.children, childTemplet]}
    }
    return item
  })
  setBookvideo(updatedList)
}

// 修改子视频字段值:需要传入父索引、子索引
const onChangeChild = (e, parentIndex, childIndex) => {
  const updatedList = bookvideo.map((item, i) => {
    if(i === parentIndex) {
      return {
        ...item,
        children: item.children.map((child, j) => {
          if(j === childIndex) {
            return {...child, [e.target.name]: e.target.value}
          }
          return child
        })
      }
    }
    return item
  })
  setBookvideo(updatedList)
}

// 删除子视频:需要传入父索引、子索引
const removeChildVideo = (parentIndex, childIndex) => {
  const updatedList = bookvideo.map((item, i) => {
    if(i === parentIndex) {
      return {
        ...item,
        children: item.children.filter((_, j) => j !== childIndex)
      }
    }
    return item
  })
  setBookvideo(updatedList)
}

// 原有主视频的操作方法保留即可,注意修改输入值绑定的问题
const onChange =(e ,index) =>{
  const updateBookvideo = bookvideo.map((item ,i) => 
    index == i ? {...item, [e.target.name]: e.target.value} : item
  );
  setBookvideo(updateBookvideo);
}
const removeFormFields =(index) =>{
  const filterbookvideo = bookvideo.filter((_, i) => i !== index);
  setBookvideo(filterbookvideo);
}
const addVideo = () =>{
  setBookvideo([...bookvideo, {...videoTemplet}])
}

3. 调整渲染逻辑

修改JSX部分的绑定逻辑,子视频列表从当前主项的children字段读取,所有操作传入对应索引:

<Accordion.Body >
  <div class="mainform bg-info">
    {bookvideo.map((element, index) => (
      <div class="container" key={index}>
        <div class="form-group col-sm-4">
          <label>Enter Chapter Name:</label>
          {/* 注意value绑定当前遍历的element属性,不要直接用bookvideo */}
          <input type="text" name="number_of_chapters"
          value={element.number_of_chapters}
          onChange={e => onChange(e,index)} class="form-control" placeholder="title" />
        </div>
        <div class="row">
          <div class="form-group col-sm-6">
            <label>Title:</label>
            <input type="text" name="video_title" value={element.video_title} onChange={e => onChange(e,index)} class="form-control"  placeholder="title" />
          </div>
          <div class="form-group col-sm-6">
            <label>Description:</label>
            <input type="text" name="description" value={element.description} onChange={e => onChange(e,index)} class="form-control"  placeholder="authors" />
          </div>
        </div>
        <div class="form-group">
          <label>Thambnail image:</label>
          <input name ="video_image" value={element.video_image} onChange={e => onChange(e,index)} type="file" class="form-control"  />
        </div>
        <div class="form-group">
          <label>Upload Video:</label>
          <input name="video_url" value={element.video_url} onChange={e => onChange(e,index)} type="file" class="form-control pb-4" />
        </div> <br></br>

        {/* 子视频从当前主项的children读取 */}
        {element.children.map((child, indexc) => (
          <div class="container bg-success" key={indexc}> 
            <div className="main">
              <div class="row">
                <div class="form-group col-sm-6">
                  <label>Title:</label>
                  <input type="text" name="video_title" value={child.video_title}    onChange={e => onChangeChild(e, index, indexc)} class="form-control"  placeholder="title" />
                </div>
                <div class="form-group col-sm-6">
                  <label>Description:</label>
                  <input type="text" name="description" value={child.description}    onChange={e => onChangeChild(e, index, indexc)} class="form-control"  placeholder="authors" />
                </div>
              </div>
              <div class="form-group">
                <label>Thambnail image:</label>
                <input  name ="video_image" value={child.video_image} onChange={e => onChangeChild(e, index, indexc)} type="file" class="form-control"  />
              </div>
              <div class="form-group">
                <label>Upload Video:</label>
                <input  name="video_url" value={child.video_url} onChange={e => onChangeChild(e, index, indexc)} type="file" class="form-control pb-4" />
              </div> <br></br>
              {
                indexc ? 
                <button type="button"   className="buttonremove remove" onClick={() => removeChildVideo(index, indexc)}>Remove</button> 
                :null
              }
            </div>
          </div>
        ))}
        <div className="button-section" style={{textAlign:"center"}}>
          {/* 新增子视频传入当前主项索引 */}
          <button className="buttonsubmit add"  type="button" onClick={() => addChildVideo(index)}><PlusOutlined /></button>
        </div>
        {
          index ? 
          <button type="button"   className="buttonremove remove" onClick={() => removeFormFields(index)}>Remove</button> 
          : null
        }
      </div>
    ))}
    <div className="button-section" style={{textAlign:"center"}}>
      <button className="buttonsubmit add"  type="button" onClick={addVideo}><PlusOutlined /></button>
    </div>
  </div>
</Accordion.Body>

修改完成后每个主视频项的子列表完全独立,新增主项时子列表默认是空的,不会显示其他主项的子视频。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 07:45:04