无法正确更新状态中的总值
Cannot properly update total value in state
我正在编写没有 redux 的简单购物车系统。
预期行为:更改产品数量时更新总金额
问题:
- 第一次渲染后总量为
0
- 删除项目后总金额不变
我需要: 优雅的解决方案。不是硬编码。
const products = [
{
id: "1",
title: "item 1",
price: "2450",
left: "14",
quantity: "1"
},
{
id: "2",
title: "item 2",
price: "2450",
left: "178",
quantity: "1"
},
{
id: "3",
title: "item 3",
price: "2450",
left: "1",
quantity: "1"
},
{
id: "4",
title: "item 4",
price: "2450",
left: "12",
quantity: "1"
}
];
class App extends React.Component {
constructor( props ) {
super( props );
this.state = {
products: this.props.products,
cart: this.props.products,
totalAmount: 0
};
this.handleRemoveProduct = this.handleRemoveProduct.bind( this );
this.handleChangeQuantity = this.handleChangeQuantity.bind( this );
this.sumTotalAmount = this.sumTotalAmount.bind( this );
}
handleRemoveProduct( id ) {
let cart = this.state.cart;
cart = cart.filter( ( product ) => {
return product.id != id;
} );
this.setState( {
cart
} );
this.sumTotalAmount();
}
handleChangeQuantity( e, id ) {
let cart = this.state.cart;
cart = cart.map( ( product ) => {
if (product.id == id ) {
product.quantity = e.target.value;
}
return product;
} );
this.setState( {
cart
} );
this.sumTotalAmount();
}
sumTotalAmount() {
let cart = this.state.cart;
let totalAmount = cart.map( ( product ) => {
return Number(product.quantity) * Number(product.price);
} ).reduce( ( total, current ) => total += current );
this.setState( {
totalAmount
} );
}
render() {
return(
<div>
<div className="cart">
{
this.state.cart.map( ( item, index ) => {
return(
<Product key={index} item={item}
handleRemoveProduct={this.handleRemoveProduct}
handleChangeQuantity={this.handleChangeQuantity}
/>
)
})
}
</div>
<div className="cart__total">
Total amount - {this.state.totalAmount}
</div>
</div>
)
}
}
const Product = ( props ) => (
<div className="cart__product">
{props.item.title} <a href="#" onClick={() => props.handleRemoveProduct(props.item.id)}>Remove</a>
<input type="number" name="quantity" min="1" max={props.item.left} value={props.item.quantity} onChange={(e) => props.handleChangeQuantity(e, props.item.id)}/>
</div>
);
ReactDOM.render(<App products = {products} />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
其实问题是你在setState之后调用了sum方法,setState是异步的,所以执行sum的时候state并没有改变,setState接收第二个参数,回调之后执行状态已更新,请参阅下面的文档 https://reactjs.org/docs/react-component.html#setstate
使 sumTotalAmount 基于 prevState 工作解决了问题
sumTotalAmount() {
this.setState( (prevState, props) => { return { totalAmount : prevState.cart.map(( product ) => {
return Number(product.quantity) * Number(product.price);
} ).reduce( ( total, current ) => total += current )}
})
}
const products = [
{
id: "1",
title: "item 1",
price: "2450",
left: "14",
quantity: "1"
},
{
id: "2",
title: "item 2",
price: "2450",
left: "178",
quantity: "1"
},
{
id: "3",
title: "item 3",
price: "2450",
left: "1",
quantity: "1"
},
{
id: "4",
title: "item 4",
price: "2450",
left: "12",
quantity: "1"
}
];
class App extends React.Component {
constructor( props ) {
super( props );
this.state = {
products: this.props.products,
cart: this.props.products,
totalAmount: 0
};
this.handleRemoveProduct = this.handleRemoveProduct.bind( this );
this.handleChangeQuantity = this.handleChangeQuantity.bind( this );
this.sumTotalAmount = this.sumTotalAmount.bind( this );
}
handleRemoveProduct( id ) {
let cart = this.state.cart;
cart = cart.filter( ( product ) => {
return product.id != id;
} );
this.setState( {
cart
} );
this.sumTotalAmount();
}
handleChangeQuantity( e, id ) {
let cart = this.state.cart;
cart = cart.map( ( product ) => {
if (product.id == id ) {
product.quantity = e.target.value;
}
return product;
} );
this.setState( {
cart
} );
this.sumTotalAmount();
}
sumTotalAmount() {
this.setState( (prevState, props) => { return { totalAmount : prevState.cart.map(( product ) => {
return Number(product.quantity) * Number(product.price);
} ).reduce( ( total, current ) => total += current )}
})
}
render() {
return(
<div>
<div className="cart">
{
this.state.cart.map( ( item, index ) => {
return(
<Product key={index} item={item}
handleRemoveProduct={this.handleRemoveProduct}
handleChangeQuantity={this.handleChangeQuantity}
/>
)
})
}
</div>
<div className="cart__total">
Total amount - {this.state.totalAmount}
</div>
</div>
)
}
}
const Product = ( props ) => (
<div className="cart__product">
{props.item.title} <a href="#" onClick={() => props.handleRemoveProduct(props.item.id)}>Remove</a>
<input type="number" name="quantity" min="1" max={props.item.left} value={props.item.quantity} onChange={(e) => props.handleChangeQuantity(e, props.item.id)}/>
</div>
);
ReactDOM.render(<App products = {products} />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
1.this.setState
是异步的,因此当您调用函数 sumTotalAmount 时状态不会得到更新,因此您将得到
所有项目的总和,即使您删除了一项。
2.Initially 您没有计算总和,因此它显示总和为 0。
因此,请尝试在 componentDidMount
中调用该函数 sumTotalAmount
。
Bruno 添加了一段代码来帮助解决问题,来自:
Total amount - {this.state.totalAmount}
收件人:
Total amount - {this.state.cart.map( ( product ) => {
return Number(product.quantity) * Number(product.price)
}).reduce( ( total, current ) => total += current )}
问题是双重的:
1) totalAmount 初始化为 0,直到您对购物车进行更改后,才会调用 sumTotalAmount 来更新它。
2) totalAmount 滞后,尤其是在最后一个 Remove 上,因为每次调用 sumTotalAmount 都依赖于状态,状态可能不是最新的(Bruno 提到的异步部分)。
我会将您的(更新的)购物车传递给 sumTotalAmount,并使用其输出来设置 totalAmount,无论是在构建还是在更改时:
constructor( props ) {
super( props );
this.handleRemoveProduct = this.handleRemoveProduct.bind( this );
this.handleChangeQuantity = this.handleChangeQuantity.bind( this );
this.sumTotalAmount = this.sumTotalAmount.bind( this );
this.state = {
products: this.props.products,
cart: this.props.products,
totalAmount: this.sumTotalAmount(this.props.products)
};
}
handleRemoveProduct( id ) {
let cart = this.state.cart;
cart = cart.filter( ( product ) => {
return product.id != id;
} );
this.setState( {
cart: cart,
totalAmount: this.sumTotalAmount(cart)
} );
//this.sumTotalAmount(cart);
}
// Make a similar change to handleChangeQuantity
sumTotalAmount(cart) {
//let cart = this.state.cart;
let totalAmount = cart.map( ( product ) => {
return Number(product.quantity) * Number(product.price);
} ).reduce( ( total, current ) => total += current );
//this.setState( {
// totalAmount
//} );
return totalAmount;
}
setState
方法是异步的,所以 this.sumTotalAmount()
几乎什么都不做。
您至少有三种方法来修复您的代码:
将求和函数作为回调参数传递给setState
(并在componentDidMount
中调用setState
)
this.setState({
cart: newCart
}, () => {
this.sumTotalAmount();
})
使求和函数成为纯函数,并使用新的购物车状态和计算出的总和调用 setState
(您基本上是在缓存总和 非透明 在州)
this.setState({
cart: newCart,
sum: calculateSum(newCart),
})
使求和函数成为纯函数,仅在您需要值时使用它(如果您使用了记忆,在这种情况下,您将透明地缓存求和 )
this.setState({
cart: newCart,
})
// in render...
<div className="cart__total">
Total amount - {calculateSum(this.state.cart)}
</div>
我正在编写没有 redux 的简单购物车系统。
预期行为:更改产品数量时更新总金额
问题:
- 第一次渲染后总量为
0
- 删除项目后总金额不变
我需要: 优雅的解决方案。不是硬编码。
const products = [
{
id: "1",
title: "item 1",
price: "2450",
left: "14",
quantity: "1"
},
{
id: "2",
title: "item 2",
price: "2450",
left: "178",
quantity: "1"
},
{
id: "3",
title: "item 3",
price: "2450",
left: "1",
quantity: "1"
},
{
id: "4",
title: "item 4",
price: "2450",
left: "12",
quantity: "1"
}
];
class App extends React.Component {
constructor( props ) {
super( props );
this.state = {
products: this.props.products,
cart: this.props.products,
totalAmount: 0
};
this.handleRemoveProduct = this.handleRemoveProduct.bind( this );
this.handleChangeQuantity = this.handleChangeQuantity.bind( this );
this.sumTotalAmount = this.sumTotalAmount.bind( this );
}
handleRemoveProduct( id ) {
let cart = this.state.cart;
cart = cart.filter( ( product ) => {
return product.id != id;
} );
this.setState( {
cart
} );
this.sumTotalAmount();
}
handleChangeQuantity( e, id ) {
let cart = this.state.cart;
cart = cart.map( ( product ) => {
if (product.id == id ) {
product.quantity = e.target.value;
}
return product;
} );
this.setState( {
cart
} );
this.sumTotalAmount();
}
sumTotalAmount() {
let cart = this.state.cart;
let totalAmount = cart.map( ( product ) => {
return Number(product.quantity) * Number(product.price);
} ).reduce( ( total, current ) => total += current );
this.setState( {
totalAmount
} );
}
render() {
return(
<div>
<div className="cart">
{
this.state.cart.map( ( item, index ) => {
return(
<Product key={index} item={item}
handleRemoveProduct={this.handleRemoveProduct}
handleChangeQuantity={this.handleChangeQuantity}
/>
)
})
}
</div>
<div className="cart__total">
Total amount - {this.state.totalAmount}
</div>
</div>
)
}
}
const Product = ( props ) => (
<div className="cart__product">
{props.item.title} <a href="#" onClick={() => props.handleRemoveProduct(props.item.id)}>Remove</a>
<input type="number" name="quantity" min="1" max={props.item.left} value={props.item.quantity} onChange={(e) => props.handleChangeQuantity(e, props.item.id)}/>
</div>
);
ReactDOM.render(<App products = {products} />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
其实问题是你在setState之后调用了sum方法,setState是异步的,所以执行sum的时候state并没有改变,setState接收第二个参数,回调之后执行状态已更新,请参阅下面的文档 https://reactjs.org/docs/react-component.html#setstate
使 sumTotalAmount 基于 prevState 工作解决了问题
sumTotalAmount() {
this.setState( (prevState, props) => { return { totalAmount : prevState.cart.map(( product ) => {
return Number(product.quantity) * Number(product.price);
} ).reduce( ( total, current ) => total += current )}
})
}
const products = [
{
id: "1",
title: "item 1",
price: "2450",
left: "14",
quantity: "1"
},
{
id: "2",
title: "item 2",
price: "2450",
left: "178",
quantity: "1"
},
{
id: "3",
title: "item 3",
price: "2450",
left: "1",
quantity: "1"
},
{
id: "4",
title: "item 4",
price: "2450",
left: "12",
quantity: "1"
}
];
class App extends React.Component {
constructor( props ) {
super( props );
this.state = {
products: this.props.products,
cart: this.props.products,
totalAmount: 0
};
this.handleRemoveProduct = this.handleRemoveProduct.bind( this );
this.handleChangeQuantity = this.handleChangeQuantity.bind( this );
this.sumTotalAmount = this.sumTotalAmount.bind( this );
}
handleRemoveProduct( id ) {
let cart = this.state.cart;
cart = cart.filter( ( product ) => {
return product.id != id;
} );
this.setState( {
cart
} );
this.sumTotalAmount();
}
handleChangeQuantity( e, id ) {
let cart = this.state.cart;
cart = cart.map( ( product ) => {
if (product.id == id ) {
product.quantity = e.target.value;
}
return product;
} );
this.setState( {
cart
} );
this.sumTotalAmount();
}
sumTotalAmount() {
this.setState( (prevState, props) => { return { totalAmount : prevState.cart.map(( product ) => {
return Number(product.quantity) * Number(product.price);
} ).reduce( ( total, current ) => total += current )}
})
}
render() {
return(
<div>
<div className="cart">
{
this.state.cart.map( ( item, index ) => {
return(
<Product key={index} item={item}
handleRemoveProduct={this.handleRemoveProduct}
handleChangeQuantity={this.handleChangeQuantity}
/>
)
})
}
</div>
<div className="cart__total">
Total amount - {this.state.totalAmount}
</div>
</div>
)
}
}
const Product = ( props ) => (
<div className="cart__product">
{props.item.title} <a href="#" onClick={() => props.handleRemoveProduct(props.item.id)}>Remove</a>
<input type="number" name="quantity" min="1" max={props.item.left} value={props.item.quantity} onChange={(e) => props.handleChangeQuantity(e, props.item.id)}/>
</div>
);
ReactDOM.render(<App products = {products} />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
1.this.setState
是异步的,因此当您调用函数 sumTotalAmount 时状态不会得到更新,因此您将得到
所有项目的总和,即使您删除了一项。
2.Initially 您没有计算总和,因此它显示总和为 0。
因此,请尝试在 componentDidMount
中调用该函数 sumTotalAmount
。
Bruno 添加了一段代码来帮助解决问题,来自:
Total amount - {this.state.totalAmount}
收件人:
Total amount - {this.state.cart.map( ( product ) => {
return Number(product.quantity) * Number(product.price)
}).reduce( ( total, current ) => total += current )}
问题是双重的:
1) totalAmount 初始化为 0,直到您对购物车进行更改后,才会调用 sumTotalAmount 来更新它。
2) totalAmount 滞后,尤其是在最后一个 Remove 上,因为每次调用 sumTotalAmount 都依赖于状态,状态可能不是最新的(Bruno 提到的异步部分)。
我会将您的(更新的)购物车传递给 sumTotalAmount,并使用其输出来设置 totalAmount,无论是在构建还是在更改时:
constructor( props ) {
super( props );
this.handleRemoveProduct = this.handleRemoveProduct.bind( this );
this.handleChangeQuantity = this.handleChangeQuantity.bind( this );
this.sumTotalAmount = this.sumTotalAmount.bind( this );
this.state = {
products: this.props.products,
cart: this.props.products,
totalAmount: this.sumTotalAmount(this.props.products)
};
}
handleRemoveProduct( id ) {
let cart = this.state.cart;
cart = cart.filter( ( product ) => {
return product.id != id;
} );
this.setState( {
cart: cart,
totalAmount: this.sumTotalAmount(cart)
} );
//this.sumTotalAmount(cart);
}
// Make a similar change to handleChangeQuantity
sumTotalAmount(cart) {
//let cart = this.state.cart;
let totalAmount = cart.map( ( product ) => {
return Number(product.quantity) * Number(product.price);
} ).reduce( ( total, current ) => total += current );
//this.setState( {
// totalAmount
//} );
return totalAmount;
}
setState
方法是异步的,所以 this.sumTotalAmount()
几乎什么都不做。
您至少有三种方法来修复您的代码:
将求和函数作为回调参数传递给
setState
(并在componentDidMount
中调用setState
)this.setState({ cart: newCart }, () => { this.sumTotalAmount(); })
使求和函数成为纯函数,并使用新的购物车状态和计算出的总和调用
setState
(您基本上是在缓存总和 非透明 在州)this.setState({ cart: newCart, sum: calculateSum(newCart), })
使求和函数成为纯函数,仅在您需要值时使用它(如果您使用了记忆,在这种情况下,您将透明地缓存求和 )
this.setState({ cart: newCart, }) // in render... <div className="cart__total"> Total amount - {calculateSum(this.state.cart)} </div>