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

React useState管理对象数组时子组件修改值无法同步到父组件

问题排查

你的代码存在4个直接导致数据不同步的问题:

  • 渲染列表时出现笔误:写的是setTodos.map(),setTodos是修改状态的函数,不是存储todo列表的状态变量,正确的遍历对象应该是todos,这处错误会直接导致列表渲染逻辑异常。
  • 子组件本地状态初始化错误:CompoTwo内部维护的a/b/c三个输入状态初始值设为空字符串,没有绑定父组件传入的todo对应字段,初始渲染就和父组件数据不同步。
  • 父组件状态更新逻辑缺失:子组件失焦触发的handleChange方法没有写完,没有把输入框的修改同步回父组件的todos状态,父组件存的一直是新增时的默认值。
  • 语法规范问题:子组件名写为小写开头的compoTwo,React会将小写开头的标签识别为原生DOM节点,直接抛渲染错误;列表用数组索引作为key,增删改时容易出现状态错位。
修复步骤
  1. 修正列表遍历的笔误,将setTodos.map改为todos.map
  2. 新增todo时生成唯一id作为数据标识,列表渲染时用id作为key,替代数组索引
  3. 子组件名改为大写开头的CompoTwo,符合React组件命名规范
  4. 子组件初始化本地状态时,直接读取父组件传入的todo字段值,保证初始数据一致
  5. 补全子组件的状态更新逻辑,通过todo的唯一id定位要修改的条目,用不可变更新的方式修改父组件的todos数组
  6. 新增todo时用函数式更新,避免连续点击触发新增时的闭包旧值问题
修复后完整代码
const CompoOne = () =>{
  const [todos, setTodos] = useState([])
  const [subject, setSubject] = useState("")
  const [priority, setPriority] = useState("")

  const addTodo = () => {
    // 函数式更新规避闭包旧值问题
    setTodos(prevTodos => [
      ...prevTodos,
      // 生成唯一id用于后续更新定位
      { 
        id: `${Date.now()}-${Math.random().toString(36).slice(2,6)}`,
        a: "Title", 
        b: "subTitle", 
        c: "content"
      }
    ])
  }

  return (
        <>
          <InputText
            value={subject}
            setValue={setSubject}
            label="subject field"
          />
          <InputText
            value={priority}
            setValue={setPriority}
            label="priority field"
          />
          <hr />
          <h2>Todo:</h2>
          <hr />
          <button onClick={addTodo}>
            add a todo
          </button>
          <>
            {/* 修正遍历对象为todos */}
            {todos.map((el)=>(
                <CompoTwo
                  todo={el}
                  setTodos={setTodos}
                  key={el.id} // 用唯一id作为key
                />
            ))}
          </>

          <button onClick={()=>console.log(todos)}>
            show all objects
          </button>
        </>
  )
}

// 组件名改为大写开头
const CompoTwo = ({ todo, setTodos }) =>{
  // 初始化时直接绑定父组件传入的todo字段值
  const [a, setA] = useState(todo.a)
  const [b, setB] = useState(todo.b)
  const [c, setC] = useState(todo.c)

  const handleChange = (e) =>{
    const {name, value} = e.target
    setTodos((prevState)=>{
      // 遍历定位当前修改的todo项,返回全新数组保证不可变更新
      return prevState.map(item => {
        if(item.id === todo.id) {
          return { ...item, [name]: value }
        }
        return item
      })
    })
  }

  return(
    <>
          <InputText
            value={a}
            name="a"
            onChange={(e)=>setA(e.target.value)}
            onBlur={handleChange}
            label="field A"
          />
          <InputText
            value={b}
            name="b"
            onChange={(e)=>setB(e.target.value)}
            onBlur={handleChange}
            label="field B"
          />
          <InputText
            value={c}
            name="c"
            onChange={(e)=>setC(e.target.value)}
            onBlur={handleChange}
            label="field C"
          />
    </>
  )
}
效果说明

修复后,新增待办项、修改子组件输入框内容触发失焦后,点击「show all objects」按钮,控制台会打印最新修改后的全量todos数据,不会再停留在新增时的默认初始值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 11:27:17