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

React组件使用defaultValue时从错误状态字段取值的问题

问题分析与修复方案

问题现象

在data_one输入框输入内容后切换选择项,data_two输入框会显示和data_one一样的内容;切回data_one时,输入框的内容也会异常复用。按逻辑,两个输入框应分别对应state中data_one.text和data_two.text的值,互不干扰。

问题原因

  1. React DOM复用机制:React为了提升性能,会尽量复用同类型的DOM元素。这里两个输入框都是<input>标签,切换时React不会销毁旧的输入框DOM,而是直接复用,导致之前输入的内容被保留下来。
  2. 错误使用defaultValue:defaultValue仅在组件首次渲染时设置输入框的值,后续状态更新不会同步到输入框;同时复用DOM时,之前输入的内容也不会被重置,这属于非受控组件写法,不适合需要与state双向同步的场景。
  3. 状态浅拷贝隐患:原代码用{...data}拷贝外层对象,但内部的data_one和data_two仍引用原对象,虽不是当前串值的主因,但长期易引发其他状态更新问题。

修复方案

方案1:给输入框添加唯一key,改为受控组件

给两个输入框设置不同的key,让React识别它们是独立元素,避免DOM复用;同时用value绑定对应状态,实现受控组件,确保输入内容与state实时同步。

修复后的代码:

const { useState } = React;

const Counter = () => {
  const [data, setData] = useState({
    displayData: "data_one",
    data_one: {
      text: ""
    },
    data_two:{
      text:""
    }
  })
  
  function handleOnChange(event){
    event.preventDefault();
    // 深度拷贝,避免内部对象引用问题
    const new_data = JSON.parse(JSON.stringify(data));
    if (event.target.name === "displayData"){
      new_data.displayData = event.target.value;
      setData(new_data);
    }
    else{
      new_data[event.target.name].text = event.target.value;
      setData(new_data);
    }
  }

  return (
    <div>
      <form onChange={handleOnChange}>
        <select name="displayData" value={data.displayData}>
          <option value="data_one">data_one</option>
          <option value="data_two">data_two</option>
        </select>
        <br/>
        {
          data.displayData === "data_one" 
            ?
            <>data One: <input name="data_one" value={data.data_one.text} key="data_one-input" /></> 
            :
            <>data two: <input name="data_two" value={data.data_two.text} key="data_two-input" /></>
        }
      </form>
    </div>
  )
}

ReactDOM.render(<Counter />, document.getElementById('app'))

方案2:同时渲染两个输入框,控制显示隐藏

不使用条件渲染,直接渲染两个输入框,通过display属性控制显示/隐藏,同样避免DOM复用问题,配合受控组件写法:

const { useState } = React;

const Counter = () => {
  const [data, setData] = useState({
    displayData: "data_one",
    data_one: {
      text: ""
    },
    data_two:{
      text:""
    }
  })
  
  function handleOnChange(event){
    event.preventDefault();
    const new_data = JSON.parse(JSON.stringify(data));
    if (event.target.name === "displayData"){
      new_data.displayData = event.target.value;
      setData(new_data);
    }
    else{
      new_data[event.target.name].text = event.target.value;
      setData(new_data);
    }
  }

  return (
    <div>
      <form onChange={handleOnChange}>
        <select name="displayData" value={data.displayData}>
          <option value="data_one">data_one</option>
          <option value="data_two">data_two</option>
        </select>
        <br/>
        <div style={{display: data.displayData === "data_one" ? "block" : "none"}}>
          data One: <input name="data_one" value={data.data_one.text} />
        </div>
        <div style={{display: data.displayData === "data_two" ? "block" : "none"}}>
          data two: <input name="data_two" value={data.data_two.text} />
        </div>
      </form>
    </div>
  )
}

ReactDOM.render(<Counter />, document.getElementById('app'))

关键修正点

  • 添加唯一key:让React区分两个输入框,避免DOM复用。
  • 用value替代defaultValue:将输入框改为受控组件,确保输入内容与state实时同步。
  • 深度拷贝状态:解决浅拷贝导致的内部对象引用问题,保证状态更新的独立性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 06:35:32