简体   繁体   中英

How to prevent my Page render when I click on the button

I am trying to optimize my react application, while profiling my application I found that when I click on Add to cart page my whole page is getting re-rendered. Could anyone help me with, how to avoid that and why it is happening?

FYR, GitHub repo: https://github.com/sandeep8080/shopping-cart-assignment

import { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import SideBar from "../../components/sideBar/SideBar";
import { getProductsData } from "../../redux/action/products";
import ProductCard from '../../components/productCard/ProductCard';
import './products.css';
import { getCategoryData } from "../../redux/action/category";
import Cart from "../cart/cart";
import Modal from '../../components/modal/Modal';
import { useHistory, useParams } from "react-router";

const ProductsPage = () => {
  const dispatch = useDispatch();
  const router = useHistory();
  const { id } = useParams();
  console.log(` product comp : ${id}`);

  const productsData = useSelector(data => data.Products.products);
  const sideBarData = useSelector(data => {
    const listItems = data.Categories.CategoriesItems;
    const activeListItems = listItems.filter(item => item.enabled === true);
    return activeListItems;
  });
  const openCart = useSelector(state => state.CartDetails.isOpen);

  const [fProductData, setFProductData] = useState([]);

  useEffect(() => {
    dispatch(getProductsData());
    dispatch(getCategoryData());
  }, []);

  useEffect(() => {
    if (id) {
      filterDataByCategory(id);
    } else {
      setFProductData(productsData);
    }
  }, [productsData, id]);

  // Function to filter out the data based on category
  const filterDataByCategory = (id) => {
    console.log("Filter data function called")
    const filterData = productsData.filter(item => item.category === id);
    setFProductData(filterData);
  };

  const handleClickProduct = useCallback((id) => {
    filterDataByCategory(id);
    router.push(`/products/${id}`);
  }, [id]);

  return (
    <div className='product-main'>
      <SideBar
        sideBarData={sideBarData}
        handleClickProduct={handleClickProduct}
      />
      <div className='product-container'>
        <div className='product-row'>
          {
            (fProductData).map((product) => {
              return (
                <div key={product.id} className='card-wrapper' >
                  <ProductCard key={product.id} {...product} />
                </div>
              )
            })
          }
        </div>
      </div>
      {
        openCart &&
        <Modal>
          <Cart />
        </Modal>
      }
    </div >
  )
};

export default ProductsPage;


// Product Card component
import './ProductCard.css';
import Button from '../button/Button';
import React from 'react';
import { useDispatch } from 'react-redux';
import { updateCart } from '../../redux/action/cart';
import priceFromatter from '../../lib/priceFromatter';
const ProductCard = ({ name, price, description, imageURL, id }) => {
  const dispatch = useDispatch();

  const handleClick = () => {
    console.log('product clicked', id);
    dispatch(updateCart(id, 'add'));
  };

  let imgURL = `../../${imageURL}`;
  // imgURL = imgURL.replace(/([^:]\/)\/+/g, "$1");
  // const image = React.lazy(() => import (`${imgURL}`));
  // console.log(image);
  return (
    <article className='card-container'>
      <h6 className='card-header'>
        {name}
      </h6>
      <div className='content-container'>
        <img
          className='content-img'
          // src={require(`${imgURL}`).default}
          src={imageURL}
        />
        <div className='content'>
          <p className='content-desc'>{description}</p>
          <div className='content-footer'>
            <p>{priceFromatter(price)}</p>
            <Button btnText='Add To Cart' handleClick={() => handleClick(id)} />
          </div>
        </div>
      </div>
    </article>
  )
};

export default ProductCard;


import { callApi } from "../../lib/api";
import { actions } from '../actionContants/actionConstant';

export const toggleCart = (isToggle) => {
  return {
    type: actions.OPEN_CART,
    payload: isToggle,
  }
};

export const updateCart = (id, operation) => {
  return async (dispatch, getState) => {

    const productList = getState().Products.products;
    const cartItems = getState().CartDetails.cartItems;

    const currItem = productList.find(({ id: currentItemId }) => currentItemId === id);
    const isItemInCart = cartItems.find(({ id }) => id === currItem.id);
    let finalItem = [];
    if (!isItemInCart) {
      finalItem = [...cartItems, { ...currItem, count: 1 }]
    } else {
      finalItem = cartItems.map(item => {
        if (item.id === currItem.id) {
          operation === 'add' ? item.count = item.count + 1 : item.count = item.count - 1
        }
        return item;
      }).filter(({ count }) => count)
    }
    try {
      const result = await callApi.post('/addToCart', id);
      result && dispatch({
        type: actions.UPDATE_TO_CART,
        payload: finalItem
      })
    } catch (error) {
      console.log(error)
    }
  }
};

In products.js change the following block of code:

const sideBarData = useSelector(data => {
  const listItems = data.Categories.CategoriesItems;
  const activeListItems = listItems.filter(item => item.enabled === true);
  return activeListItems;
});

to:

const sideBarData = useSelector(data => {
  const listItems = data.Categories.CategoriesItems;
  const activeListItems = listItems.filter(item => item.enabled === true);
  return activeListItems;
}, shallowEqual);

useSelector will force a component to re-render when the selector returns a new reference that is different than the previous reference (it uses the === operator). Ref: https://react-redux.js.org/api/hooks#equality-comparisons-and-updates . As you are filtering the array returned from the store, it will always be a different object reference to the one in the store.

The use of shallowEqual as the equalityFn to useSelector() can be used to change the comparison and prevent an unnecessary re-render of the <ProductsPage> component.

did you try using e.preventDefault() otherwise the answer above might work

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM