我的代码如下所示,您可以看到它在https://codepen.io/rongeegee/pen/BaVJjGO中是如何工作的:
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 = {...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" defaultValue={data.data_one.text} /></>
:
<>data two: <input name="data_two" defaultValue={data.data_two.text} /></>
}
</form>
</div>
)
}
ReactDOM.render(<Counter />, document.getElementById('app'))
如果我在data_one的输入中键入一些内容,在“data_one”和“data_two”之间切换值,data_two输入字段将具有相同的值。如果我更改data_one中的值,将下拉列表切换到“data_one”,data_one将再次具有相同的值。
这不应该发生,因为data_one input使用data state中data_one字段的文本字段的值,而data_two input使用data_two字段的文本字段的值。不应该从state中的另一个字段获取值。
3条答案
按热度按时间vcudknz31#
React有一种方法来确定哪些元素/组件已经更改,哪些没有更改。这是必要的,因为DOM操作的开销很大,所以我们应该尽可能地限制它。这就是为什么React有一种方法来确定在重新呈现之间哪些更改了,哪些没有更改;并且只更改DOM中更改的内容。
因此,如果我们在您的案例中从dataOne切换到dataTwo,React会执行类似以下操作:“哦,不错,
input
元素仍然是input
元素。不错,我不需要完全破坏DOM节点并从头开始渲染它,我可以检查更改了什么并更改它。让我们看看:名称已经更改......所以让我们更改它,但除此之外,一切都保持不变。”(这意味着您的input元素不会被销毁,另一个元素也不会被初始渲染,但一个input元素得到的是名称更改,React就到此为止了--由于default Value
仅适用于元素/DOM节点的初始创建,因此不会显示它)React重新呈现内容和比较/修改DOM的方式相当复杂。为了获得更多信息,我可以重新播放以下视频:https://youtu.be/i793Qm6kv3U(它确实帮助我更好地理解了整个React Render过程)。
解决这个问题的一个可能的方法是给予每个输入元素一个键,这样你的输入元素看起来就像这样:
所以是的,修复相当容易;然而,理解我们为什么需要这个修复的原因并不容易。
vptzau2j2#
关于你的评论(我的回答是渴望评论,格式在回答中更好)
不,你没有在输入字段改变时改变状态...你正在改变/操作状态变量
data
...但是你没有更新你的状态。因此没有触发重新渲染,并且如果由其他东西触发重新渲染,数据将会丢失。所以你实际上没有改变状态。只能通过调用useState-Hook提供的回调函数来更改状态。(在您的示例中,useState-Hook提供的回调函数是
setData
。在您的else
语句中,您没有调用提供的回调函数,而只是操作data
对象(因为您进行了浅层克隆,因此操作了原始的data
对象)当改变状态时,一定要使用提供的回调函数
const [stateVariable, thisIsTheCallback] = useState(initVal)
。如果你不使用这个回调函数,而只是操纵stateVariable,你迟早会遇到问题(调试这个问题特别乏味,因为看起来你在改变状态(因为stateVariable改变了),但实际上你并没有改变状态,因为只有回调函数可以做到这一点)uqzxnwby3#
将
defaultValue
属性替换为value
属性,一切都会正常。要了解
defaultValue
在状态更改后无法更新的原因,请阅读**@Lord-JulianXLII**的第一个答案