编辑:由于代码片段没有重现bug -这里有一个github repo的链接:(代码远未完成)
https://github.com/altruios/clicker-game
我现在已经在两台计算机上运行了它--两台计算机都有代码片段没有显示的相同行为。
第一个
所以我构建了一个clicker游戏来学习reaction,我不明白为什么这段代码会这样:
在主应用程序中,我有这个功能:
gainResource(event)
{
console.count("gain button");
const name = event.target.name;
this.setState( (prevState)=>
{
const newResources = prevState.resources.map(resource=>
{
if(resource.name === name)
{
resource.amount = Number(resource.amount) + 1 //Number(prevState.clickers.find(x=>x.name===name).value)
}
return resource;
});
console.log(prevState.resources.find(item=>item.name===name).amount, "old");
console.log(newResources.find(item=>item.name===name).amount, "new");
return {resources: newResources}
});
}
这个console.count只运行一次...但是我得到了2个“旧的和新的”对。就好像setState在这个只运行一次的函数中运行了两次一样?
控制台输出为:
App.js:64 gain button: 1
App.js:76 1 "old"
App.js:77 1 "new"
App.js:76 2 "old"
App.js:77 2 "new"
所以看起来函数运行了一次。但是set状态运行了两次?
症状是它以2递增计数。而且amount的初始状态是0,而不是1,如gamedata.json中所示
resourceData:
[
{
name:"grey",
resouceMax:100,
isUnlocked:true,
changePerTick:0,
counterTillStopped:100,
amount:0
},{etc},{},{}],
clickerData:
[
{
name:"grey",
subjectsOfIncrease:["grey"],
isUnlocked:true,
value:1
},{etc},{},{}]
我不认为我接下来要写的代码与此行为相关,但我还不知道如何React,所以我不知道我错过了什么:但我是这样生成clicker按钮的:
const clickers = this.state.clickers.map(clickerData=>
{
return(
<Clicker
name={clickerData.name}
HandleClick = {this.gainResource}
value = {clickerData.amount}
key={clickerData.name}
/>
)
})
在clicker.js函数组件中,我只返回以下内容:
<div>
{props.name}
<button name={props.name} onClick={props.HandleClick}>
{props.name} {props.value}
</button>
</div>
函数在构造函数中绑定到this...我不明白为什么在一个只调用一次的函数中运行两次setState。
我也试过:
<div>
{props.name}
<button name={props.name} onClick={()=>props.HandleClick}> //anon function results in no output
{props.name} {props.value}
</button>
</div>
5条答案
按热度按时间4c8rllxm1#
最佳答案:
我使用create-react-app,我的App Component被 Package 在Strict模式下...这会触发setState两次...这完美地解释了为什么这在代码片段中不可重现,以及为什么函数被调用一次,而setState被调用两次。
移除严格模式完全修复了该问题。
r6hnlfcb2#
这是 Package 在****组件中的**setState(callback)**方法的预期行为<React.Strict>。
回调执行两次,以确保它不会直接改变状态。
在程式码片段中,您会建立新的数组,但数组中的对象仍然相同:
您必须单独复制每个对象:
xggvc2p63#
只要你没有给我们提供一个可运行的例子,我对可能发生的事情有一个疑问,让我们看看它是否有效。
我可以看到的是在*gainResource*函数中,特别是在这一行中*resource.amount = Number(resource.amount)+ 1*您正在尝试在不使用setState的情况下更新状态,这是React Documentation不推荐的
请改为尝试先指定const myRessource = ressource,然后再传回myRessource。
ecr0jaav4#
好吧......所以在拔了一些头发之后......我发现了一种有效的方法......但我不认为这是“最佳实践”,但现在当我写这篇文章时,它对我很有效:
对比
没有prevState函数的setState只被调用一次...而有prevState函数的setState被调用两次...为什么?
所以我仍然不明白为什么setState使用一个带有prevState的函数会在一个只调用一次的函数中导致两次函数调用......我已经读到我应该使用prevstate作为this.state.resources.slice();只是拍了一个“不定时的快照”,可能是不可靠的。这是真的吗...还是这种方法可以接受?
这是一个答案,任何其他人挣扎与此。希望一个更好的答案可以张贴后,这个启示可能会发生什么。
nfg76nw05#
好吧,我花了一点时间来弄清楚这个问题。正如其他人提到的,你的回调函数必须是幂等的。这里要意识到的是,react每次调用它时都会将相同的状态对象示例传递到你的回调函数中。因此,如果你在第一次调用时更改了状态对象,那么在第二次调用时它将是不同的,你的回调函数将不是幂等的。
上面的情况可能很明显,但在处理数组时,这种情况就不那么明显了。