【问题标题】:How could I prevent my product list to rerender while parent component cart state updates?如何防止我的产品列表在父组件购物车状态更新时重新呈现?
【发布时间】:2020-02-26 23:53:57
【问题描述】:

我有一个 Main.js 组件,该组件路由到各种组件,包括 Listing.js

  • 主要:它包含作为状态的一组添加到购物车的产品。

  • 列表:这是产品列表。它包含数据库中所有产品的状态。

我的问题:当我通过单击 List 组件中的按钮将产品添加到购物车时,它会将产品添加到购物车,更新状态 cart 主要组件。这样做,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>
        )
    }
}

【问题讨论】:

    标签: javascript reactjs components react-component react-state-management


    【解决方案1】:

    在 react 的匿名函数中包装元素会导致该元素在每次渲染时重新实例化(在某些情况下)。

    我认为问题在于您如何使用Route 组件。使用 children 属性可能会更直观。

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

    【讨论】:

      【解决方案2】:

      如果您将匿名函数传递给 Route 中的 component 属性,它每次都会重新渲染。 相反,将您的路线设置为:

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

      引用反应路由器docs:

      当你使用组件(而不是渲染或子组件,如下)时,路由器使用 React.createElement 从给定组件创建一个新的 React 元素。这意味着如果您为组件属性提供内联函数,您将在每次渲染时创建一个新组件。这会导致现有组件卸载和新组件安装,而不是仅更新现有组件。使用内联函数进行内联渲染时,请使用 render 或 children 属性

      【讨论】:

      • 我在这个问题上耽搁了这么多时间,你真是个天才! :D
      猜你喜欢
      • 2020-02-21
      • 2013-09-24
      • 2015-01-17
      • 2015-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多