Как рассчитать сумму предметов и общую стоимость с React? - PullRequest
0 голосов
/ 15 января 2019

У меня есть приложение реакции, где я получаю информацию о продуктах из файла Json. Они отображаются правильно, и кнопки увеличения-уменьшения работают хорошо.

Итак, в index.js три компонента js называются main.js, header.js, footer.js.

Main получает файл json, создает контейнер и строку, затем вызывает 8 раз (потому что в Json 8 элементов) product.js и In 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 голосов
/ 15 января 2019

Я думаю, что здесь вам не хватает концепции 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 к методу рендеринга и затем поработаем над этими функциями:

// 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. Кроме того, нам нужно иметь возможность передавать события увеличения и уменьшения:

// Main

...

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 (количество и общее количество) и 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"));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...