I am using react portal to render inside dom element rendered by parent element
import { useRef, useState } from "react";
import { createPortal } from "react-dom";
export default function App() {
const pageTitleRef = useRef(null);
const [page, setPage] = useState(true);
return (
<div className="App">
<header>
<h1>
App-<span id="page-title" ref={pageTitleRef}></span>
</h1>
<button
onClick={(e) => {
setPage((p) => !p);
}}
>
toggle page
</button>
</header>
{page ? (
<Page1 key="page1" pageTitleRef={pageTitleRef} />
) : (
<Page2 key="page2" pageTitleRef={pageTitleRef} />
)}
</div>
);
}
function Page1({ pageTitleRef }) {
return (
<div className="Page1">
{createPortal("Page2 title", pageTitleRef.current)}
<h2>Page 1 content!</h2>
</div>
);
}
function Page2({ pageTitleRef }) {
return (
<div className="Page2">
{createPortal("Page1 title", pageTitleRef.current)}
<h2>Page 2 content!</h2>
</div>
);
}
however it throws an error Target container is not a DOM element.
https://codesandbox.io/s/xenodochial-mendeleev-b5pbr?file=/src/App.js
The problem is that you're trying to use pageTitleRef.current before it has anything in it. It doesn't have anything in it until App renders, but during the rendering of App, you're trying to call createPortal (from Page1 and Page2).
You'll need to do some refactoring; some options:
Page1 and Page2 reach out to it via createPortal; App already knows what page it's on.App.App state and pass a setter function to Page1 and Page2 they can use to set that content.I'd go with #1, Page1 and Page2 seem to have too many responsibilities (page header and page body). You could split them up into PageHead1/PageHead2 and PageBody1/PageBody2. But if it makes more sense, either #2 or #3 could work too.