调用 this.setState 后重置函数变量值
Function variable value resetting after calling this.setState
我是 JavaScript 世界的新手,我正在学习 React 并且遇到了一个奇怪的问题
查看此代码
addIngredientHandler = (type) => {
let oldCount = this.state.ingredients[type];
let copyState = {...this.state.ingredients};
let newPrice = 0;
copyState[type] = oldCount + 1;
this.setState( (prevState, prevProps) => {
newPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
console.log('newprice inside setState: ' + newPrice);
return { ingredients: copyState, totalPrice: newPrice}
} );
console.log('newprice outside setState: ' + newPrice);
this.updatePurchaseable(copyState, newPrice);
}
这里我关心的是 newPrice 变量,它用于在添加更多项目时更新状态,效果很好
问题出在 this.setState
return newPrice 再次重新测试为 0
所以我不能将它用于底部的功能。
是的,我可以直接使用状态变量,但由于 setState
执行的异步性质,我想改为传递变量值。
在控制台中,由于 setState
的异步特性,您可以看到首先执行外部控制台日志,然后执行内部控制台日志
也许我没有得到生成此类行为的某些生命周期反应。
这里是状态值,值应该无关紧要,但为了更好的画面
state = {
ingredients: {
salad: 0,
bacon: 0,
meat: 0,
cheese: 0,
},
purchasable: false,
totalPrice: 0
}
任何提示都有帮助,感谢阅读。
调用setState
后newPrice
等于0
的原因是React状态更新是异步。状态更新后的代码会运行beforesetState
实际做一些事情,所以在调用this.updatePurchaseable(copyState, newPrice);
阶段所有的计算newPrice
尚未执行。
BTW - 这也是为什么您的 console.log
以“相反”顺序打印的原因,每次呈现外部日志都会在内部日志之前打印。
对于这个特定的代码示例,我建议您尝试将现在在 setState
回调中的所有计算移到它之外,甚至移到另一个函数。
试试这个 -
calculateNewPrice = (totalPrice, type) => {
newPrice = totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
}
addIngredientHandler = (type) => {
const { totalPrice } = this.state;
let oldCount = this.state.ingredients[type];
let copyState = {...this.state.ingredients};
copyState[type] = oldCount + 1;
const newPrice = calculateNewPrice(totalPrice, type);
this.setState({ ingredients: copyState, totalPrice: newPrice });
this.updatePurchaseable(copyState, newPrice);
}
React 状态更新是 异步 但 setState
功能是完全 同步,所以当你调用updatePurchaseable
时newPrice
还没有更新。将所有额外的“状态更新后”逻辑移动到 componentDidUpdate
生命周期方法中,这样您就可以 access/reference 更新后的 totalPrice
并使用更新后的状态调用 updatePurchaseable
。
componentDidUpdate(prevProps, prevState) {
if (prevState.totalPrice !== this.state.totalPrice) {
const { ingredients, totalPrice } = this.state;
console.log('newprice outside setState: ' + totalPrice);
this.updatePurchaseable(ingredients, totalPrice);
}
}
addIngredientHandler = (type) => {
this.setState((prevState, prevProps) => {
let newPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
return {
ingredients: {
...prevState.ingredients,
[type]: prevState.ingredients[type] + 1,
},
totalPrice: newPrice
}
});
}
this.setState()
被异步调用,因此您不能依赖 this.state
在调用 this.setState()
后立即引用更新的值。通读 FAQ on component state.
状态更新后如果想引用newPrice
的更新值,可以:
- 使用
componentDidUpdate()
生命周期方法。参见 https://reactjs.org/docs/react-component.html#componentdidupdate。
addIngredientHandler = (type) => {
let oldCount = this.state.ingredients[type];
let copyState = { ...this.state.ingredients };
let newPrice = 0;
copyState[type] = oldCount + 1;
this.setState((prevState) => {
newPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
return { ingredients: copyState, totalPrice: newPrice }
});
}
componentDidUpdate(prevProps, prevState) {
if (prevState.totalPrice !== this.state.totalPrice) {
this.updatePurchaseable(this.state.ingredients, this.state.totalPrice);
}
}
- 使用
this.setState()
的第二个参数。请参阅 https://reactjs.org/docs/react-component.html#setstate. 上的文档
addIngredientHandler = (type) => {
let oldCount = this.state.ingredients[type];
let copyState = { ...this.state.ingredients };
let newPrice = 0;
copyState[type] = oldCount + 1;
this.setState((prevState) => {
newPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
return { ingredients: copyState, totalPrice: newPrice }
}, () => {
this.updatePurchaseable(this.state.ingredients, this.state.totalPrice);
});
}
- 使用
ReactDOM.flushSync()
。参见 https://github.com/reactwg/react-18/discussions/21。
import { flushSync } from 'react-dom';
addIngredientHandler = (type) => {
let oldCount = this.state.ingredients[type];
let copyState = { ...this.state.ingredients };
let newPrice = 0;
copyState[type] = oldCount + 1;
flushSync(() => {
this.setState((prevState) => {
newPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
return { ingredients: copyState, totalPrice: newPrice }
});
});
this.updatePurchaseable(copyState, newPrice);
}
如果我要编写此方法,我建议使用 componentDidUpdate
生命周期方法,因为这将确保在总价发生变化时始终调用 updatePurchaseable
。如果您只在事件处理程序内部调用 updatePurchaseable
,那么如果价格在该处理程序之外发生变化,您可能会遇到错误。
addIngredientHandler = (type) => {
this.setState(prevState => {
let totalPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
totalPrice = Math.round(totalPrice * 100) / 100;
return {
ingredients: {
...prevState.ingredients,
[type]: prevState.ingredients[type] + 1,
},
totalPrice,
};
});
}
componentDidUpdate(prevProps, prevState) {
const { totalPrice, ingredients } = this.state;
if (prevState.totalPrice === totalPrice) {
/*
Bail early. This is a personal code style preference. It may
make things easier to read as it keeps the main logic on the
"main line" (un-nested / unindented)
*/
return;
}
/*
If `updatePurchaseable` is a class method then you don't need to
pass state to it as it will already have access to `this.state`.
If `updatePurchaseable` contains complicated business logic,
consider pulling it out into its own module to make it easier
to test.
*/
this.updatePurchaseable(ingredients, totalPrice);
}
我是 JavaScript 世界的新手,我正在学习 React 并且遇到了一个奇怪的问题 查看此代码
addIngredientHandler = (type) => {
let oldCount = this.state.ingredients[type];
let copyState = {...this.state.ingredients};
let newPrice = 0;
copyState[type] = oldCount + 1;
this.setState( (prevState, prevProps) => {
newPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
console.log('newprice inside setState: ' + newPrice);
return { ingredients: copyState, totalPrice: newPrice}
} );
console.log('newprice outside setState: ' + newPrice);
this.updatePurchaseable(copyState, newPrice);
}
这里我关心的是 newPrice 变量,它用于在添加更多项目时更新状态,效果很好
问题出在 this.setState
return newPrice 再次重新测试为 0
所以我不能将它用于底部的功能。
是的,我可以直接使用状态变量,但由于 setState
执行的异步性质,我想改为传递变量值。
在控制台中,由于 setState
也许我没有得到生成此类行为的某些生命周期反应。
这里是状态值,值应该无关紧要,但为了更好的画面
state = {
ingredients: {
salad: 0,
bacon: 0,
meat: 0,
cheese: 0,
},
purchasable: false,
totalPrice: 0
}
任何提示都有帮助,感谢阅读。
调用setState
后newPrice
等于0
的原因是React状态更新是异步。状态更新后的代码会运行beforesetState
实际做一些事情,所以在调用this.updatePurchaseable(copyState, newPrice);
阶段所有的计算newPrice
尚未执行。
BTW - 这也是为什么您的 console.log
以“相反”顺序打印的原因,每次呈现外部日志都会在内部日志之前打印。
对于这个特定的代码示例,我建议您尝试将现在在 setState
回调中的所有计算移到它之外,甚至移到另一个函数。
试试这个 -
calculateNewPrice = (totalPrice, type) => {
newPrice = totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
}
addIngredientHandler = (type) => {
const { totalPrice } = this.state;
let oldCount = this.state.ingredients[type];
let copyState = {...this.state.ingredients};
copyState[type] = oldCount + 1;
const newPrice = calculateNewPrice(totalPrice, type);
this.setState({ ingredients: copyState, totalPrice: newPrice });
this.updatePurchaseable(copyState, newPrice);
}
React 状态更新是 异步 但 setState
功能是完全 同步,所以当你调用updatePurchaseable
时newPrice
还没有更新。将所有额外的“状态更新后”逻辑移动到 componentDidUpdate
生命周期方法中,这样您就可以 access/reference 更新后的 totalPrice
并使用更新后的状态调用 updatePurchaseable
。
componentDidUpdate(prevProps, prevState) {
if (prevState.totalPrice !== this.state.totalPrice) {
const { ingredients, totalPrice } = this.state;
console.log('newprice outside setState: ' + totalPrice);
this.updatePurchaseable(ingredients, totalPrice);
}
}
addIngredientHandler = (type) => {
this.setState((prevState, prevProps) => {
let newPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
return {
ingredients: {
...prevState.ingredients,
[type]: prevState.ingredients[type] + 1,
},
totalPrice: newPrice
}
});
}
this.setState()
被异步调用,因此您不能依赖 this.state
在调用 this.setState()
后立即引用更新的值。通读 FAQ on component state.
状态更新后如果想引用newPrice
的更新值,可以:
- 使用
componentDidUpdate()
生命周期方法。参见 https://reactjs.org/docs/react-component.html#componentdidupdate。
addIngredientHandler = (type) => {
let oldCount = this.state.ingredients[type];
let copyState = { ...this.state.ingredients };
let newPrice = 0;
copyState[type] = oldCount + 1;
this.setState((prevState) => {
newPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
return { ingredients: copyState, totalPrice: newPrice }
});
}
componentDidUpdate(prevProps, prevState) {
if (prevState.totalPrice !== this.state.totalPrice) {
this.updatePurchaseable(this.state.ingredients, this.state.totalPrice);
}
}
- 使用
this.setState()
的第二个参数。请参阅 https://reactjs.org/docs/react-component.html#setstate. 上的文档
addIngredientHandler = (type) => {
let oldCount = this.state.ingredients[type];
let copyState = { ...this.state.ingredients };
let newPrice = 0;
copyState[type] = oldCount + 1;
this.setState((prevState) => {
newPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
return { ingredients: copyState, totalPrice: newPrice }
}, () => {
this.updatePurchaseable(this.state.ingredients, this.state.totalPrice);
});
}
- 使用
ReactDOM.flushSync()
。参见 https://github.com/reactwg/react-18/discussions/21。
import { flushSync } from 'react-dom';
addIngredientHandler = (type) => {
let oldCount = this.state.ingredients[type];
let copyState = { ...this.state.ingredients };
let newPrice = 0;
copyState[type] = oldCount + 1;
flushSync(() => {
this.setState((prevState) => {
newPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
newPrice = Math.round(newPrice * 100) / 100;
return { ingredients: copyState, totalPrice: newPrice }
});
});
this.updatePurchaseable(copyState, newPrice);
}
如果我要编写此方法,我建议使用 componentDidUpdate
生命周期方法,因为这将确保在总价发生变化时始终调用 updatePurchaseable
。如果您只在事件处理程序内部调用 updatePurchaseable
,那么如果价格在该处理程序之外发生变化,您可能会遇到错误。
addIngredientHandler = (type) => {
this.setState(prevState => {
let totalPrice = prevState.totalPrice + PRICES_OF_INGREDIENTS[type];
totalPrice = Math.round(totalPrice * 100) / 100;
return {
ingredients: {
...prevState.ingredients,
[type]: prevState.ingredients[type] + 1,
},
totalPrice,
};
});
}
componentDidUpdate(prevProps, prevState) {
const { totalPrice, ingredients } = this.state;
if (prevState.totalPrice === totalPrice) {
/*
Bail early. This is a personal code style preference. It may
make things easier to read as it keeps the main logic on the
"main line" (un-nested / unindented)
*/
return;
}
/*
If `updatePurchaseable` is a class method then you don't need to
pass state to it as it will already have access to `this.state`.
If `updatePurchaseable` contains complicated business logic,
consider pulling it out into its own module to make it easier
to test.
*/
this.updatePurchaseable(ingredients, totalPrice);
}