I have a Main.js component, that route to various components, including Listing.js.
Main: it contains as state an array of the products added to the cart.
List: it's a listing of the products. It contains as state all the products from database.
My problem: when I add a product to the cart by clicking on a button in List component, it adds the product to the cart, updating the state cart of Main component. Doing so, the List component rerenders, and I loose all the filters the visitor seted on the listing.
I'd like to prevent List from rerender when cart state of its parent component changes. Do you have any idea of how to do that ? I've tried with shouldComponentUpdate, but not succesfull.
Main.js (parent component)
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, listing of product:
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>
)
}
}
Wrapping elements in anonymous functions in react will cause that element to be re-instantiated on every render (in some cases).
I think the issue lies with how you're using the Route
component. Using the children
prop might make this more intuitive.
<Route path='/listing'>
<List addToCart={this.addToCart} />
</Route>