无法正确更新州的总价值

时间:2018-05-30 15:11:01

标签: javascript reactjs react-dom

我正在编写没有redux的简单购物车系统。

预期行为:更改产品数量时更新总金额

问题

  1. 首次渲染总金额为0
  2. 之后
  3. 删除项目总金额后不会改变
  4. 我需要:优雅的解决方案。不是硬编码。

    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>

4 个答案:

答案 0 :(得分:2)

实际上,问题是你在setState之后调用sum方法并且setState是异步的,所以当执行sum时,状态没有改变,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 )}
      })
  }

&#13;
&#13;
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"));
&#13;
<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>
&#13;
&#13;
&#13;

答案 1 :(得分:2)

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滞后,特别是在最后一次删除时,因为每次调用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;
}

答案 2 :(得分:2)

setState方法是异步的,因此this.sumTotalAmount()几乎不会做任何事情。 您至少有三种方法来修复代码:

  • 将sum函数作为回调参数传递给setState(并在setState中调用componentDidMount

    this.setState({
        cart: newCart
    }, () => {
        this.sumTotalAmount();
    })
    
  • 使sum函数变为纯函数并使用新的购物车状态和计算的总和调用setState(您基本上缓存非透明处于状态)

    this.setState({
        cart: newCart,
        sum: calculateSum(newCart),
    })
    
  • 将sum函数设为纯函数并仅在需要值时使用它(如果使用了memoization,在这种情况下,您将缓存透明地

    this.setState({
        cart: newCart,
    })
    
    // in render...
    <div className="cart__total">       
      Total amount - {calculateSum(this.state.cart)}
    </div>
    

答案 3 :(得分:0)

1。this.setState是异步的,所以当你调用函数sumTotalAmount时,状态不会得到更新,所以你会得到 即使您删除了一个项目,所有项目的总和。

2.最初你没有计算总和,所以显示总和为0。  因此,请尝试在sumTotalAmount中调用该函数componentDidMount