首页 > 解决方案 > 如何使用 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

标签: jsonreactjsfetch

解决方案


我认为您在这里缺少 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组件的渲染添加到 render 方法中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() { return ( { this.props.products.map( (product, index) => this.props.onIncrement(index) } onDecrement={ () => this.props.onDecrement(index) } /> ) } ) }

...

Product组件中,我们现在不再需要内部状态,因为我们需要的一切都作为 props 提供,所以它可以是无状态组件:

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(数量和总计)和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组件:

import React from "react";
import ReactDOM from "react-dom";
import App from "./components/app";
import './index.css';
import 'bootstrap/dist/css/bootstrap.css';


ReactDOM.render(<App />, document.getElementById("root"));

推荐阅读