如何使用React计算项目总和和总价?

时间:2019-01-15 16:46:48

标签: json reactjs fetch

我有一个React应用,可从Json文件中获取“产品”详细信息。它们显示正确,并且“递增-递减”按钮可以正常工作。

因此在index.js中,三个js组件分别称为main.js,header.js和footer.js。

Main获取json文件,然后创建容器并行,然后调用8次(因为Json中存在8个项目)product.js,在Product.js中,有关产品和单个按钮的所有信息都显示在页面上。

这是我的问题: 使每个物料数量乘以相关价格并在标题中添加总数量和总价格的最简单方法是什么?

索引

import React from "react";
import ReactDOM from "react-dom";
import Main from "./components/main";
import Footer from "./components/footer";
import Header from "./components/header";
import './index.css';
import 'bootstrap/dist/css/bootstrap.css';


ReactDOM.render(<Main />, document.getElementById("root"));
ReactDOM.render(<Header />, document.getElementById("header"));
ReactDOM.render(<Footer />, document.getElementById("footer"));

标题

import React, { Component } from "react";




class header extends Component {
    state = {
        totalPrice: 200,
        totalQuantity:0
    };
    render() {
        return (
            <div>
                <nav className="navbar navbar-expand-lg navbar-dark bg-info">
                    <a className="navbar-brand" href="#">
                        <img src="./logo.png" id="logo" alt="" />
                    </a>
                    <button
                        className="navbar-toggler"
                        type="button"
                        data-toggle="collapse"
                        data-target="#navbarNavDropdown"
                        aria-controls="navbarNavDropdown"
                        aria-expanded="false"
                        aria-label="Toggle navigation"
                    >
                        <span className="navbar-toggler-icon" />
                    </button>
                    <div className="collapse navbar-collapse" id="navbarNavDropdown">
                        <ul className="navbar-nav">
                            <li className="nav-item active">
                                <a className="nav-link" href="#">
                                    Home <span className="sr-only">(current)</span>
                                </a>
                            </li>
                            <li className="nav-item">
                                <a className="nav-link" href="#">
                                    Features
                </a>
                            </li>
                            <li className="nav-item">
                                <a className="nav-link" href="#">
                                    Pricing
                </a>
                            </li>

                        </ul>


                    </div>
                    <input className="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search"></input>
                    <button className="btn btn-success m-2" type="submit">Search</button>
                    <h2><span className={this.getClass()}>
                        Total Quantity:
                        Total Price: {this.formatCount()} 
                    </span></h2>

                </nav>
            </div>
        );
    }
    getClass() {
        let classes = "badge";
        classes += this.state.totalPrice === 0 ? " badge-danger" : " badge-warning";
        return classes;
    }

    formatCount() {
        const { totalPrice } = this.state;
        return totalPrice === 0 ? "Your Cart is Empty" : totalPrice+"€";
    }
}

export default header;

主要

import React, { Component } from 'react';
import ProductInfo from '../plist.json';
import Product from './product'



class Products extends Component {


  render() {
    return (

        <div className="container">
          <div className="row ">
          {ProductInfo.map(postDetail => <Product {...postDetail} />)}
          </div>
        </div>

    )
  }
}

export default Products

产品

import React, { Component } from 'react';


class Product extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        };

    }


    handleIncerement = () => {
        this.setState({
            count: this.state.count + 1
        });

    }

    handleDecrement = () => {
        if(this.state.count< 1){
            this.setState({
              count:0
            });
          }else {
            this.setState({
              count: this.state.count- 1
            });
          }
    }

    render() {
        const { name, image, price, description } = this.props;
        let totalQuantity= 0;
        let totalPrice = 0;
        totalQuantity += this.state.count;
        totalPrice += this.state.count * {price};
        console.log("Quantity:"+ totalQuantity);
        console.log("Total Price:"+ totalPrice);



        return (
            <div className="col-md-4 ml-auto">
                <img className="productpic" src={require(`./images/${image}`)} alt="Product" />
                <h2 className="display-6"> <a href="{url}">{name}</a></h2>
                <p className="h5 price">{price}</p>
                <p className="info">{description}</p>
                <div className="counter">
                    <button className="btn btn-info" onClick={this.handleIncerement}>+</button>
                    <div className="count">{this.state.count}</div>
                    <button className="btn btn-info" onClick={this.handleDecrement}>-</button>
                </div>
            </div>
        );
    }
}

export default Product

1 个答案:

答案 0 :(得分:1)

我认为您在这里缺少React的概念。如果需要以下内容,则应在组件层次结构中保持较高的状态。

在此示例中,您需要在同级组件Main中使用组件Header中的某些内容。这意味着您应该具有将这些信息传递给他们的父组件。

例如,您可以拥有一个App组件,该组件以某种方式采用JSON并将其与其他产品信息一起保持其状态:

// App.js
import React, { Component } from 'react'
import PRODUCTS from '../plist.json'

class App extends Component {

  state = {
    // here we are preparing the state copying all the
    // information of a product plus a quantity property set to 0
    products: PRODUCTS.map(p => ({ ...p, quantity: 0 }))
  }

  render() {
    return (
      <>
        {/* here we should render the two components who needs data */}
        <Footer />
      </>
    )
  }

}

render方法中,我们可以渲染三个初始组件,但要进行一些更改...

首先,Header需要总数量和总价格。 React最佳实践之一告诉我们,可以根据状态计算的所有内容都应该在状态之外。在这种情况下,我们不需要将这两个数量保存在状态中,因为我们可以轻松地计算它们:

// in App class definition

...

totalQuantity = () =>
  this.state.products.reduce(
    (sum, product) => sum + product.quantity,
    0
  )

totalPrice = () =>
  this.state.products.reduce(
    (sum, product) => sum + product.quantity * product.price,
    0
  )

...

为了能够计算这些值,我们将Header组件的渲染添加到App的渲染方法中:

// in App class definition

...

render() {
  return (
    <>
      <Header quantity={ this.totalQuantity() } 
              price={ this.totalPrice() }
      />
      {/* here we should render the Main component */}
      <Footer />
    </>
  )
}

...

当然,您必须更改在Header组件中呈现这些值的方式:

// Header component, render() method
// remember to apply some formatting for currency etc.
<span className={ this.getClass() }>
  Total Quantity: { this.props.quantity }
  Total Price: { this.props.price } 
</span>

现在,让我们重新考虑一下Main组件。它有两件事:

  • 呈现产品列表;
  • 处理数量的增加/减少;

让我们将Main添加到render方法中,然后使用以下功能:

// in App class definition

...

render() {
  return (
    <>
      <Header quantity={ this.totalQuantity() } 
              price={ this.totalPrice() }
      />
      <Main products={ this.state.products }
            onIncrement={ this.handleIncrement }
            onDecrement={ this.handleDecrement }
      />
      {/* here we should render the Footer component */}
    </>
  )
}

...

Main组件中,我们需要更改呈现产品的方式,因为我们不再读取JSON,但是可以使用App提供的数据。另外,我们需要能够传递增量和减量事件:

//主要

...

render(){   返回(                     {           this.props.products.map(             (产品,索引)=>                this.props.onIncrement(index)}                        onDecrement = {()=> this.props.onDecrement(index)}               />           )         }               ) }

...

Product组件中,我们现在不再需要内部状态,因为我们需要的所有内容都是作为道具提供的,因此它可以是无状态组件:

const Product = ({
  image, 
  url, 
  name, 
  price, 
  description, 
  onIncrement, 
  quantity,
  onDecrement
}) => (
  <div className="col-md-4 ml-auto">
    <img className="productpic" 
         src={ require(`./images/${image}`) }
         alt="Product"
    />
    <h2 className="display-6">
      <a href="{url}">
        { name }
      </a>
    </h2>
    <p className="h5 price">
      { price }
    </p>
    <p className="info">
      { description }
    </p>
    <div className="counter">
      <button className="btn btn-info"
              onClick={ onIncrement }>
        +
      </button>
      <div className="count">
        { quantity }
      </div>
      <button className="btn btn-info"
              onClick={ onDecrement }>
        -
      </button>
    </div>
  </div>
)

要完成此行为,我们需要处理App组件中的增量和减量,以便更新状态并将更新的信息传播到Header(数量和总数)和{{1 }}。

Main

我们几乎完成了您的// in App ... handleIncrement = index => this.setState(prevState => ({ products: [ ...prevState.products, [index]: { ...prevState.products[index], quantity: prevState.products[index].quantity + 1 } ] })) handleDecrement = index => this.setState(prevState => ({ products: [ ...prevState.products, [index]: { ...prevState.products[index], quantity: prevState.products[index].quantity - 1 } ] })) ... 中的index.js组件的渲染:

App