Как можно предотвратить повторное отображение списка товаров при обновлении состояния корзины родительского компонента? - PullRequest
0 голосов
/ 31 октября 2019

У меня есть компонент Main.js , который направляет к различным компонентам, включая Listing.js .

  • Main: он содержита указать массив продуктов, добавленных в корзину.

  • Список: это список продуктов. Он содержит в качестве состояния все товары из базы данных.

Моя проблема: когда я добавляю товар в корзину, нажимая кнопку в компоненте списка, он добавляеттовар в корзину, обновление состояния корзина из основной компонент. При этом компонент List перезапускается, и я теряю все фильтры, которые посетитель установил в листинге.

Я бы хотел предотвратить повторное отображение List при изменении состояния корзины его родительского компонента. У вас есть идеи, как это сделать? Я пытался с shouldComponentUpdate, но не успешно.

Main.js (родительский компонент)

import React from 'react';
import {Switch, Route, withRouter, Link} from "react-router-dom";
import {List} from "./Listing/List";

class Main extends React.Component
{
    state={
        cart:[],
    };

    removeFromCart = product => //REMOVES PRODUCT FROM CART
    {
        let cart = this.state.cart;
        cart.map(item => {
            if(item._id === product._id)
            {
                item.count--;
                return item;
            }
        });
        cart = cart.filter(item => item.count > 0);
        this.setState({cart:cart}, () => {sessionStorage.setItem('cart', JSON.stringify(cart));});
    };

    addToCart = product => //ADD PRODUCT TO CART
    {
        let cart = this.state.cart;
        let productExists = this.state.cart.map(item => {return item._id === product._id}).includes(true);

        if(productExists)
        {
            cart = cart.map(item => {
                if(item._id === product._id)
                {
                    item.count++;
                    return item;
                }
                else
                {
                    return item;
                }
            });
        }
        else
        {
            product.count = 1;
            cart.push(product);
        }

        this.setState({cart: cart}, () => {sessionStorage.setItem('cart', JSON.stringify(cart));});
    };

    componentWillMount()
    {
        if(sessionStorage.getItem('cart')) this.setState({cart:JSON.parse(sessionStorage.getItem('cart'))});
    }

    render() {
        return (
            <div className='main'>
                <Header cart={this.state.cart} />
                <Switch>
                    <Route path='/listing' component={() => <List addToCart={this.addToCart} />} />
                </Switch>
            </div>
        );
    }
}

export default withRouter(Main);

List.js , перечень продукции:

import React from "react";
import {Product} from "./Product";
import Data from '../../Utils/Data';
import {Search} from "./Search/Search";

export class List extends React.Component
{

    state = {
        serie: '',
        products: [],
        filteredProducts: [],
    };

    addToCart = this.props.addToCart;

    obtainProducts = (request = {}) => //searches for products in database
    {
        Data.products.obtain(request).then(products => {
            this.setState({products:products, filteredProducts: products});
        });
    };

    displayProducts = () => {
        //Only products that has title
        const products = this.state.filteredProducts.filter(product => {return product.title;});

        //Returns REACT COMPONENT
        return products.map(product => {
            return <Product
                key={product._id}
                product={product}
                addToCart={this.addToCart}
            />
        });
    };

    searchHandler = (collection, types) =>
    {

        let filteredProducts = this.state.products;
        if(collection.length)
            filteredProducts = filteredProducts.filter(product => {return collection.includes(product.series);});
        if(types.length)
            filteredProducts = filteredProducts.filter(product => {return types.includes(product.type);});

        this.setState({filteredProducts: filteredProducts});
    };

    componentWillMount()
    {
        //init products collection
        this.obtainProducts();
    }

    render()
    {
        const productComponents = this.displayProducts();
        console.log('test');
        return(
            <section className='listing'>
                <Search searchHandler={this.searchHandler} />
                <div className='listing-content grid-4 has-gutter'>
                    {productComponents}
                </div>
            </section>
        )
    }
}

Ответы [ 2 ]

2 голосов
/ 31 октября 2019

Если вы передадите анонимную функцию в component prop в Route, она будет перерисовываться каждый раз. Вместо этого установите ваш маршрут как:

<Route path='/listing' render={() => <List addToCart={this.addToCart} />} />

Цитирование из реагирующего маршрутизатора Документы :

Когда вы используете компонент (вместо рендера или дочерних элементов, ниже)маршрутизатор использует React.createElement для создания нового элемента React из данного компонента. Это означает, что если вы предоставите встроенную функцию для компонента prop, вы будете создавать новый компонент при каждом рендеринге. Это приводит к размонтированию существующего компонента и монтированию нового компонента вместо простого обновления существующего компонента. При использовании встроенной функции для встроенного рендеринга используйте рендер или дочернюю опору

1 голос
/ 31 октября 2019

Обтекание элементов в анонимных функциях в реакции приведет к тому, что этот элемент будет повторно создаваться при каждом рендеринге (в некоторых случаях).

Я думаю, что проблема заключается в том, как вы используете компонент Route,Использование children prop может сделать это более интуитивным.

<Route path='/listing'>
 <List addToCart={this.addToCart} />
</Route>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...