'Why do createPortal renders the whole component?

I have a problem with the createPortal function. When I use the "Modal", then by changing the states, the whole "Modal" component will be rendered. Can you help me how to avoid this? So when I comment out the Modal wrap in the Cart component (as I did it below), it works as I expected, but with the Modal wrap, when the states are changed, not the component will be re-rendered, but always the whole Modal Component

Here is my Modal component with createPortal function:

import React from 'react';
import { createPortal } from 'react-dom';
import { useState } from 'react';
import './modal.scss';

export default function Modal({ children, onClick }) {
  const BackDrop = () => {
    return <div className='backdrop' onClick={onClick}></div>;
  };

  

  const Modal = () => {
    return (
      <div className='modal'>
        <div className='content'>{children}</div>
      </div>
    );
  };

  return (
    <>
      {createPortal(<BackDrop />, document.getElementById('modal'))}
      {createPortal(<Modal />, document.getElementById('modal'))}
    </>
  );
}

The Cart component which uses the Modal component:

import React, { useEffect } from 'react';
import Modal from '../UI/Modal';
import './cart.scss';
import { useCartContext } from '../../store/CartContext';
import CartItem from './CartItem';

export default function Cart({ onHideCart }) {
  const { cart, totalPrice, updateTotalPrice, addToCartOne, removeFromCartOne } = useCartContext();

  useEffect(() => {
    updateTotalPrice();
  }, [totalPrice, cart]);

  const onAddHandler = (id) => {
    addToCartOne(id);
    updateTotalPrice();
  };

  const onRemoveHandler = (id) => {
    removeFromCartOne(id);
    updateTotalPrice();
  };

  return (
    // <Modal onClick={onHideCart}>
    <div>
      <ul className='cart-items'>
        {cart.map((item, idx) => (
          <CartItem
            key={item.id}
            name={item.name}
            price={item.price}
            amount={item.amount}
            onAdd={onAddHandler.bind(null, item.id)}
            onRemove={onRemoveHandler.bind(null, item.id)}
          />
        ))}
      </ul>
      <div className='total'>
        <span>Total Amount</span>
        <span>$ {totalPrice.toFixed(2)}</span>
      </div>
      <div className='actions'>
        <button className='button--alt' onClick={onHideCart}>
          Close
        </button>
        <button className='button'>Order</button>
      </div>
    </div>
    // </Modal>
  );
}

enter image description here

So by changing the amount with + and - buttons, the html element with id modal, always renders, it's flashes in the devtools... but when I comment out the Modal wrap in the Cart component, there is no flashes by modal ID. I hope it makes sense.



Solution 1:[1]

The problem was with the two custom element inside of the Cart element. When I return

createPortal(
    <>
      <div className='backdrop' onClick={onClick}></div>
      <div className='modal'>
        <div className='content'>{children}</div>
      </div>
    </>,
    document.getElementById('modal')
  )

instead of this:

<>
   {createPortal(<BackDrop />, document.getElementById('modal'))}
   {createPortal(<Modal />, document.getElementById('modal'))}
</>

Then there is no problem with rendering.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dharman