为什么当将两个或多个相同的对象连续添加到状态时,它们总是生成相同的键?
Why, when adding two or more identical objects to state in a row, they always generate the same key?
我正在学习 React,但遇到了这个问题。我一直在寻找答案,但找不到确切的原因。
当我填写并提交表单时,状态中添加了一个新对象。无论我如何生成随机密钥(例如,在下面的代码中,我使用日期对象和 getTime 方法来生成随机密钥),当我将两个或多个相同的对象连续添加到状态时(一个完全接一个),它们总是生成相同的密钥,与 math.random 和我尝试过的所有其他方法相同。
如果我添加一个对象,然后稍微更改任何数据并添加另一个对象,然后添加与第一个对象相同的对象,一切都会完美无缺。唯一的问题是当我尝试连续添加相同的时。
我所说的相同对象是指它们的所有 {key: value} 对都包含相同的数据并且它们具有相同的键。
我的状态对象是这样的:
state = {
dmd: [
{name: 'dmd', age: 69, rich: 'yes', id: 1},
{name: 'lk', age: 19, rich: 'true', id: 2},
{name: 'tm', age: 83, rich: 'aye', id: 3}
]
}
请注意,当我已经对这个初始状态进行硬编码时,当我添加一个与第三个对象相同的新对象时,不会发生错误。就是新建的时候。
传递给 addDmd 方法的 newDmd 对象起初没有 id,看起来像这样:
{name: 'john', age: 22, rich: 'yes'}
用于改变状态的方法:
addDmd = (newDmd) => {
newDmd.id = new Date().getTime();
let newArray = [...this.state.dmd, newDmd];
this.setState({
dmd: newArray
});
}
下图是当key变成一样的时候给大家看的(我的key是'id')。可以看到最后3次相加,key是一样的,从第二次开始重复:
我没有足够的声誉点数来post一张图片,所以这是一张link:https://i.imgur.com/WQrVHlp.png
我想知道:
- 为什么会这样
- 如何解决这个问题
- 关于更改状态和生成唯一键的最佳实践
编辑:
从调用 addDmd 方法的地方添加代码。
class AddDmd extends Component {
state = {
name: null,
age: null,
rich: null
};
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
});
}
handleSubmit = (e) => {
e.preventDefault();
this.props.addDmd(this.state);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label htmlFor="name">Name:</label>
<input type="text" id="name" onChange={this.handleChange}></input>
<label htmlFor="age">Age:</label>
<input type="text" id="age" onChange={this.handleChange}></input>
<label htmlFor="rich">rich?:</label>
<input type="text" id="rich" onChange={this.handleChange}></input>
<button>Submit</button>
</form>
</div>
)
}
}
编辑 2:在 addDmd 方法所在的位置添加了 class 的完整代码。
class App extends Component {
state = {
dmd: [
{name: 'dmd', age: 69, rich: 'yes', id: 1},
{name: 'lk', age: 19, rich: 'true', id: 2},
{name: 'tm', age: 83, rich: 'aye', id: 3}
]
}
addDmd = (newDmd) => {
newDmd.id = new Date().getTime();
let newArray = [...this.state.dmd, newDmd];
this.setState({
dmd: newArray
});
}
render() {
return (
<div className="App" >
<AddDmd addDmd={this.addDmd} />
</div>
);
}
}
您的问题很可能与通过引用意外更改嵌套对象有关。
1.为什么会这样:
我现在只能推测。如果您想分享 addDmd
的用法,我可以确认。我相信 newDmd
是从以前的条目填充的。这意味着做 newDmd.id =
会改变状态中的两个对象。
您可以通过在每次添加新条目时检查 id
来自己确认这一点。新的 id
应该有所不同,因为它是基于时间的,但请注意查看以前的条目是否会随之变化。
2。修复方法:
将您的函数更改为此,以便它对状态进行深度复制:
addDmd = (newDmd) => {
newDmd.id = new Date().getTime();
let newArray = [...this.state.dmd.map(nestedObj => ({...nestedObj})), newDmd];
this.setState({
dmd: newArray
});
}
3。最佳实践:
据我所知,您生成唯一键的方法应该可以正常工作。我建议的最佳做法是确保在必要时对状态进行深度复制。
更新
我非常接近。问题是您将 this.state
直接传递给 addDmd
。 this.state
每次组件重新呈现时都会获得一个新的对象引用,但如果您不更改表单中的任何值并再次提交 - 它不需要重新呈现。这意味着您传递 addDmd
相同的对象引用。
简单修复:
handleSubmit = (e) => {
e.preventDefault();
this.props.addDmd({...this.state}); // Create new object out of state
}
现在您可以根据需要多次提交相同的表单,它总是会提交一个新的对象引用,并且不会改变之前提交的内容。
为什么会这样?
我完全同意@Brian Thompsan 提到的原因。
此外,我已经为您的用例创建了一个示例代码,我在单击按钮时添加了具有不同 ID 的相同项目(使用 date/time 创建)。它工作正常,这是代码。
import React from "react";
import Listbox from "./listBox";
class App extends React.Component {
constructor() {
super();
this.state = {
dmd: [
{ name: "dmd", age: 69, rich: "yes", id: 1 },
{ name: "lk", age: 19, rich: "true", id: 2 },
{ name: "tm", age: 83, rich: "aye", id: 3 }
]
};
}
addDmd = () => {
const newId = new Date().getTime();
console.log(newId);
let newArray = [
...this.state.dmd,
{ id: newId, name: "new name", age: "new age" }
];
this.setState({
dmd: newArray
});
};
render() {
return (
<>
<input type="button" onClick={this.addDmd} />
{this.state.dmd.map(item => (
<div>
id: {item.id} name:: {item.name}
</div>
))}
</>
);
}
}
export default App;
我正在学习 React,但遇到了这个问题。我一直在寻找答案,但找不到确切的原因。
当我填写并提交表单时,状态中添加了一个新对象。无论我如何生成随机密钥(例如,在下面的代码中,我使用日期对象和 getTime 方法来生成随机密钥),当我将两个或多个相同的对象连续添加到状态时(一个完全接一个),它们总是生成相同的密钥,与 math.random 和我尝试过的所有其他方法相同。
如果我添加一个对象,然后稍微更改任何数据并添加另一个对象,然后添加与第一个对象相同的对象,一切都会完美无缺。唯一的问题是当我尝试连续添加相同的时。
我所说的相同对象是指它们的所有 {key: value} 对都包含相同的数据并且它们具有相同的键。
我的状态对象是这样的:
state = {
dmd: [
{name: 'dmd', age: 69, rich: 'yes', id: 1},
{name: 'lk', age: 19, rich: 'true', id: 2},
{name: 'tm', age: 83, rich: 'aye', id: 3}
]
}
请注意,当我已经对这个初始状态进行硬编码时,当我添加一个与第三个对象相同的新对象时,不会发生错误。就是新建的时候。
传递给 addDmd 方法的 newDmd 对象起初没有 id,看起来像这样:
{name: 'john', age: 22, rich: 'yes'}
用于改变状态的方法:
addDmd = (newDmd) => {
newDmd.id = new Date().getTime();
let newArray = [...this.state.dmd, newDmd];
this.setState({
dmd: newArray
});
}
下图是当key变成一样的时候给大家看的(我的key是'id')。可以看到最后3次相加,key是一样的,从第二次开始重复:
我没有足够的声誉点数来post一张图片,所以这是一张link:https://i.imgur.com/WQrVHlp.png
我想知道:
- 为什么会这样
- 如何解决这个问题
- 关于更改状态和生成唯一键的最佳实践
编辑: 从调用 addDmd 方法的地方添加代码。
class AddDmd extends Component {
state = {
name: null,
age: null,
rich: null
};
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
});
}
handleSubmit = (e) => {
e.preventDefault();
this.props.addDmd(this.state);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label htmlFor="name">Name:</label>
<input type="text" id="name" onChange={this.handleChange}></input>
<label htmlFor="age">Age:</label>
<input type="text" id="age" onChange={this.handleChange}></input>
<label htmlFor="rich">rich?:</label>
<input type="text" id="rich" onChange={this.handleChange}></input>
<button>Submit</button>
</form>
</div>
)
}
}
编辑 2:在 addDmd 方法所在的位置添加了 class 的完整代码。
class App extends Component {
state = {
dmd: [
{name: 'dmd', age: 69, rich: 'yes', id: 1},
{name: 'lk', age: 19, rich: 'true', id: 2},
{name: 'tm', age: 83, rich: 'aye', id: 3}
]
}
addDmd = (newDmd) => {
newDmd.id = new Date().getTime();
let newArray = [...this.state.dmd, newDmd];
this.setState({
dmd: newArray
});
}
render() {
return (
<div className="App" >
<AddDmd addDmd={this.addDmd} />
</div>
);
}
}
您的问题很可能与通过引用意外更改嵌套对象有关。
1.为什么会这样:
我现在只能推测。如果您想分享 addDmd
的用法,我可以确认。我相信 newDmd
是从以前的条目填充的。这意味着做 newDmd.id =
会改变状态中的两个对象。
您可以通过在每次添加新条目时检查 id
来自己确认这一点。新的 id
应该有所不同,因为它是基于时间的,但请注意查看以前的条目是否会随之变化。
2。修复方法:
将您的函数更改为此,以便它对状态进行深度复制:
addDmd = (newDmd) => {
newDmd.id = new Date().getTime();
let newArray = [...this.state.dmd.map(nestedObj => ({...nestedObj})), newDmd];
this.setState({
dmd: newArray
});
}
3。最佳实践:
据我所知,您生成唯一键的方法应该可以正常工作。我建议的最佳做法是确保在必要时对状态进行深度复制。
更新
我非常接近。问题是您将 this.state
直接传递给 addDmd
。 this.state
每次组件重新呈现时都会获得一个新的对象引用,但如果您不更改表单中的任何值并再次提交 - 它不需要重新呈现。这意味着您传递 addDmd
相同的对象引用。
简单修复:
handleSubmit = (e) => {
e.preventDefault();
this.props.addDmd({...this.state}); // Create new object out of state
}
现在您可以根据需要多次提交相同的表单,它总是会提交一个新的对象引用,并且不会改变之前提交的内容。
为什么会这样? 我完全同意@Brian Thompsan 提到的原因。
此外,我已经为您的用例创建了一个示例代码,我在单击按钮时添加了具有不同 ID 的相同项目(使用 date/time 创建)。它工作正常,这是代码。
import React from "react";
import Listbox from "./listBox";
class App extends React.Component {
constructor() {
super();
this.state = {
dmd: [
{ name: "dmd", age: 69, rich: "yes", id: 1 },
{ name: "lk", age: 19, rich: "true", id: 2 },
{ name: "tm", age: 83, rich: "aye", id: 3 }
]
};
}
addDmd = () => {
const newId = new Date().getTime();
console.log(newId);
let newArray = [
...this.state.dmd,
{ id: newId, name: "new name", age: "new age" }
];
this.setState({
dmd: newArray
});
};
render() {
return (
<>
<input type="button" onClick={this.addDmd} />
{this.state.dmd.map(item => (
<div>
id: {item.id} name:: {item.name}
</div>
))}
</>
);
}
}
export default App;