为什么这个 onClick 事件处理程序在我的 create-react-app 中触发两次
Why is this onClick event handler firing twice in my create-react-app
有人能告诉我为什么这个“upvote”onClick 处理程序会触发两次吗?
日志将表明它只有 运行 一次,但它控制的分数增加了 2
export default class Container extends Component {
constructor(props) {
super(props);
this.state = {
jokes: [],
};
this.getNewJokes = this.getNewJokes.bind(this);
this.retrieveJokes = this.retrieveJokes.bind(this);
this.upVote = this.upVote.bind(this);
}
upVote(id) {
this.setState(state => {
//find the joke with the matching id and increase score by one
const modifiedJokes = state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
return joke;
});
console.log(modifiedJokes);
return { jokes: modifiedJokes };
});
}
render() {
return (
<div>
<h1>Container</h1>
{this.state.jokes.map(joke => (
<Joke
key={joke.id}
id={joke.id}
joke={joke.joke}
score={joke.score}
upVote={this.upVote}
downVote={this.downVote}
/>
))}
</div>
);
}
}
另一方面,如果我以这种方式重写处理程序,那么它只会触发一次
upVote(id) {
const modifiedJokes = this.state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
return joke;
});
this.setState({ jokes: modifiedJokes });
};
这行不通,因为 React 使用合成事件,这些事件在处理完成后会被重用。调用 setState
的函数形式将延迟评估(当 setState
调用依赖于事件并且事件已失去其值时,这可能很危险)。
所以这也应该有效:
this.setState((state,props) => ({ jokes: modifiedJokes}));
我现在找不到我的来源,但是,我记得不久前遇到过这个。我确信浏览 React 文档可以提供更深入的解释
我最好的猜测是,在第一种情况下,您也在直接修改状态,当您执行 joke.score = joke.score + 1;
因为您直接在状态数组变量上进行此映射,而在 Javascript 中,当使用数组时,您仅使用指向该数组的指针,而不是创建该数组的副本。
所以映射函数可能采用数组的浅表副本,这就是问题所在。
您可以在使用 lodash 之前创建状态数组的深层副本,这不会导致您的问题:
我发现了问题所在。我会 post 一个答案,以防万一有人在遇到同样的问题时偶然发现这个问题。
在调试模式下,React 会 运行 某些功能两次,以阻止某些可能导致错误的操作。这些操作之一是直接修改状态。
做的时候
this.setState(state => {
//find the joke with the matching id and increase score by one
const modifiedJokes = state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
return joke;
});
console.log(modifiedJokes);
return { jokes: modifiedJokes };
映射函数returns一个新数组,其中每个元素都指向原始数组中的相同元素。因此通过
state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
我实际上是在直接修改状态。在调试模式下响应 运行 setstate 函数两次以清除此类操作。
所以修改嵌套在数组中的对象的属性的正确“反应方式”是改为这样做
this.setState(state =>{
let modifiedJokes = state.jokes.map(joke => {
if (joke.id === id) {
return {...joke, score:joke.score+1}
}
else{ return joke}
})
return {jokes:modifiedJokes}
})
这样,当遇到具有正确 ID 的元素时,会对该特定元素进行复制,并且该副本的分数会增加一个,这不会影响仍处于未触及状态的实际元素,直到它通过提交新状态的反应来修改
有人能告诉我为什么这个“upvote”onClick 处理程序会触发两次吗? 日志将表明它只有 运行 一次,但它控制的分数增加了 2
export default class Container extends Component {
constructor(props) {
super(props);
this.state = {
jokes: [],
};
this.getNewJokes = this.getNewJokes.bind(this);
this.retrieveJokes = this.retrieveJokes.bind(this);
this.upVote = this.upVote.bind(this);
}
upVote(id) {
this.setState(state => {
//find the joke with the matching id and increase score by one
const modifiedJokes = state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
return joke;
});
console.log(modifiedJokes);
return { jokes: modifiedJokes };
});
}
render() {
return (
<div>
<h1>Container</h1>
{this.state.jokes.map(joke => (
<Joke
key={joke.id}
id={joke.id}
joke={joke.joke}
score={joke.score}
upVote={this.upVote}
downVote={this.downVote}
/>
))}
</div>
);
}
}
另一方面,如果我以这种方式重写处理程序,那么它只会触发一次
upVote(id) {
const modifiedJokes = this.state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
return joke;
});
this.setState({ jokes: modifiedJokes });
};
这行不通,因为 React 使用合成事件,这些事件在处理完成后会被重用。调用 setState
的函数形式将延迟评估(当 setState
调用依赖于事件并且事件已失去其值时,这可能很危险)。
所以这也应该有效:
this.setState((state,props) => ({ jokes: modifiedJokes}));
我现在找不到我的来源,但是,我记得不久前遇到过这个。我确信浏览 React 文档可以提供更深入的解释
我最好的猜测是,在第一种情况下,您也在直接修改状态,当您执行 joke.score = joke.score + 1;
因为您直接在状态数组变量上进行此映射,而在 Javascript 中,当使用数组时,您仅使用指向该数组的指针,而不是创建该数组的副本。
所以映射函数可能采用数组的浅表副本,这就是问题所在。
您可以在使用 lodash 之前创建状态数组的深层副本,这不会导致您的问题:
我发现了问题所在。我会 post 一个答案,以防万一有人在遇到同样的问题时偶然发现这个问题。
在调试模式下,React 会 运行 某些功能两次,以阻止某些可能导致错误的操作。这些操作之一是直接修改状态。 做的时候
this.setState(state => {
//find the joke with the matching id and increase score by one
const modifiedJokes = state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
return joke;
});
console.log(modifiedJokes);
return { jokes: modifiedJokes };
映射函数returns一个新数组,其中每个元素都指向原始数组中的相同元素。因此通过
state.jokes.map(joke => {
if (joke.id === id) {
joke.score = joke.score + 1;
}
我实际上是在直接修改状态。在调试模式下响应 运行 setstate 函数两次以清除此类操作。
所以修改嵌套在数组中的对象的属性的正确“反应方式”是改为这样做
this.setState(state =>{
let modifiedJokes = state.jokes.map(joke => {
if (joke.id === id) {
return {...joke, score:joke.score+1}
}
else{ return joke}
})
return {jokes:modifiedJokes}
})
这样,当遇到具有正确 ID 的元素时,会对该特定元素进行复制,并且该副本的分数会增加一个,这不会影响仍处于未触及状态的实际元素,直到它通过提交新状态的反应来修改