3

I am using portal in React and want to get access to elements from other components.

For example, I have components with:

// here I want to put some content via portal
<div id='buttons'></div>

That is my component where I create portal

  const elem = document.getElementById('buttons');

     return (
    <div className="block-mainContent">
      {isLoading ?
        <Loader />
        :
        <Table
          data={memoCustomers}
          headers={headers}
          actionRender={actionRender}
          showCheckbox
          onSelect={onSelect}
          onSelectAll={onSelectAll}
          selectedRows={Array.from(selectedIds)}
        />}
      <Modal
        isOpen={isOpen}
        onClose={() => dispatch(closeModalAction())}
        content={editModalContent()}
      />
      {!!elem && createPortal(<button>test</button>, elem)} // !!! here is my portal !!!
    </div>
  );

My problem is I get such error "Target container is not a DOM element

As I understand, the problem happens because I try to get an element before it is mounted and as result I get null.

How to check if my div elemen exists and then to create portal?

I try to use different approaches. In the code above I use that

 {!!elem && createPortal(<button>test</button>, elem)} 

// here I check If element exists it will return true and portal will be created

// but it works a bit wrong. Describe it below

It helps to remove error, but my portal starts working only at the second render. I mean, after loading my page there is no portal. If only I make smth on the page, rerender happens and portal appears.

I tried else

 const [mounted, setMounted] = useState(false)
  useEffect(() => {
    setMounted(true)
  }, []);

{mounted && createPortal(<button>test</button>, elem)}

// here I create portal in the first render via useEffect and 

But it throws the same error. No one of my ideas works.

If you know how to fix it, thank you in advance!

2 Answers 2

2

Try with useRef for the portal DOM node.

// parent component

const buttonsContainerRef = useRef();

<div ref={buttonsContainerRef}>{/* buttons */}</div>
<MainContent buttonsContainer={buttonsContainerRef.current} />

// main content

{buttonsContainer ? createPortal(<button />, buttonsContainer) : null}
Sign up to request clarification or add additional context in comments.

1 Comment

It is possible to do that without ref? My element div is in separate component. And to get this ref via props is very inconvenient.
1

I think you need to create your buttons element to the parent component which of you need to create portal, or html body in index.html.

For example

// index.html in public folder if 
<html>
  <body>
    <div id="root"></div>
    <div id='buttons'></div>
  </body>
  ...
</html>

or in your parent component without using useRef and follow your previous coding style

import React, { useEffect, useState } from "react";
import { createPortal } from "react-dom";

const PortalContainer = () => {
  return <div id={"buttons"}></div>;
};

const Home = () => {
  const [mounted, setMounted] = useState(false);
  const [portalElement, setPortalElement] = useState();

  useEffect(() => {
    setMounted(true);
  }, []);


  useEffect(() => {
    const portalContainer = document.getElementById("buttons");
    if (portalContainer)
      setPortalElement(createPortal(<button>test</button>, portalContainer));
  }, [mounted]);

  return (
    <>
      <h2>Portal Testing</h2>
      <PortalContainer />
      {portalElement}
    </>
  );
};

export default Home;

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.