There seems to be an issue with ReactDOM.createPortal() generating redundant empty divs in next.js with TypeScript

This is the backdrop component in TypeScript:

interface BacdropProps {
  open?: string;
  onClick: () => void;
}

const Backdrop: React.FC<BacdropProps> = (props) => {
  let container: HTMLDivElement | null = null;
  if (typeof window !== "undefined") {
    const rootContainer = document.createElement("div");
    const parentElem = document.querySelector("#__next");
    parentElem?.insertAdjacentElement("afterend", rootContainer);
    container = rootContainer;
  }

  return container
    ? ReactDOM.createPortal(
        <div
          className={["backdrop", props.open ? "open" : ""].join(" ")}
          onClick={props.onClick}
        />,
        container
      )
    : null;
};

export default Backdrop;

This is the CSS for the Backdrop component:

.backdrop {
  width: 100%;
  height: 100vh;
  background: rgba(0, 0, 0, 0.75);
  z-index: 100;
  position: fixed;
  left: 0;
  top: 0;
  transition: opacity 0.3s ease-out;
  opacity: 1;
}

This is an image of how it looks: https://i.sstatic.net/umwK7.png

Answer №1

Your code is currently creating div.backdrop every time the Backdrop component re-renders. The correct approach should be to create it only once. This can be achieved by using useEffect to ensure that ReactDOM.createPortal is executed just once. Additionally, you can use useRef to maintain the same instance of the container across renders.

const containerRef = useRef<HTMLDivElement>(null);

useEffect({
  // Executed only once on the client side
  if (typeof window !== "undefined") {
    const rootContainer = document.createElement("div");
    const parentElem = document.querySelector("#__next");
    parentElem?.insertAdjacentElement("afterend", rootContainer);
    containerRef.current = rootContainer;
  }
}, [window])

useEffect({
  // Executed once when containerRef is bound to <HTMLDivElement>
  if(containerRef.current) {
    ReactDOM.createPortal(
      <div
        className={["backdrop", props.open ? "open" : ""].join(" ")}
        onClick={props.onClick}
      />,
      containerRef.current
    )
  }
}, [containerRef])

Edit

  1. I removed the check for the existence of window, as useEffect will only run on the client-side.

  2. Since ReactDOM.createPortal creates the div.backdrop outside the root HTMLElement (div#next), returning null in the Backdrop component should suffice.

const containerRef = useRef<HTMLDivElement>(null);

useEffect({
  // useEffect runs only on the client side
  const rootContainer = document.createElement("div");
  const parentElem = document.querySelector("#__next");
  parentElem?.insertAdjacentElement("afterend", rootContainer);
  containerRef.current = rootContainer;
}, [])

useEffect({
  // Executed once when containerRef is bound to <HTMLDivElement>
  if(containerRef.current) {
    ReactDOM.createPortal(
      <div
        className={["backdrop", props.open ? "open" : ""].join(" ")}
        onClick={props.onClick}
      />,
      containerRef.current
    )
  }
}, [containerRef])

return null;

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The recharts error message displays: "There is no overload that matches this call."

I am currently working on developing a chart in react using the recharts library. To guide me, I am referencing an example provided in their documentation available at this link: https://codesandbox.io/s/zen-ellis-30cdb?file=/src/App.tsx While the project ...

Reacting with Node.js: Capturing a selected option from a dropdown menu and storing it in the database

On my React frontend, I have a select dropdown like this: <select name="level" value={level} onChange={this.handleChange} className="form-control"> <option>Begineer</option> <option>Intermediate</option> <option> ...

Incorporating photos within the layout of a Twitter Bootstrap grid

Looking to showcase images like the ones found here: https://i.stack.imgur.com/eD4GD.jpg These images are original and come in various sizes. I want a hover effect that includes blur, fogging effects, and text placed in the middle of the picture. Check ou ...

Error TS2322: The object with properties "ready: false" and "session: null" cannot be assigned to the type "Readonly<S & withAuthState>"

Here is the interface I'm currently working with: export interface withAuthState { ready: boolean, session: any } Additionally, I have developed the following Higher Order Component (HOC): const withAuth = <P extends withAuthProps, S extends ...

Arranging arrangements in javascript

I am dealing with objects that contain the fields id and position. const items = [{id: 11, position: 1}, {id: 12, position: 2}, {id: 13, position: 3}, {id: 14, position: 4}, {id: 15, position: 5}, {id: 16, position: 6}]; These objects represent folders st ...

Custom value in Field for radio type in Redux form

Can anyone help me figure out how to input text into a radio field using Redux form? Here are the fields I am working with: <Field name={some.name1} value={'text1'} component={renderRadioElement} type="radio" /> <Field name= ...

Waiting for an HTTP request to complete during server-side rendering with Angular 5 Universal

Currently, I am working on an Angular 5 application that is being served using ngExpressEngine (built off the Angular Universal starter project). Within my app, there is a component that makes an HTTP request to fetch some data for display purposes. Ever ...

The instanceof operator does not recognize the value as an instance and is returning false, even though it

Is there a method to verify the current instance being used? This is what I am logging to the console: import { OrthographicCamera } from 'three'; // Later in the file: console.log(camera instanceof OrthographicCamera, camera); and the result ...

Encountered an error while compiling following the 'npm start' command in the create-react-kotlin

Having an issue running the create-react-kotlin-app module using npm. Here's the error I'm encountering: Failed to compile multi ./node_modules/react-dev-utils/webpackHotDevClient.js ./node_modules/react-scripts-kotlin/config/polyfills.js kot ...

Creating a sleek layout with dropdowns and checkboxes aligned side by side in MVC 5 using Bootstrap 4

The current controls are structured as follows: <div class="form-horizontal"> <div class="form-group "> @Html.LabelFor(model => model.TribeId, @Resource.TribeName, htmlAttributes: new { @class = "control-label col-md-2" ...

Displaying two div elements horizontally using Material-UI

Can the styled utility from mui be used to align 2 divs side by side? The documentation examples don't seem to cover this specific scenario. While answers on this and this address it, they don't relate to mui / material ui. I attempted the fol ...

Simultaneously operating the front and backend in Electron

I'm looking to run my express backend and reactjs frontend at the same time in an electron app. I'm new to both react and electron, so the solution might be straightforward. Here's the question: What are some possible methods for achieving ...

Tips for resolving the React ENOENT issue: Whenever I attempt to run npm, I am faced with the following error message: 'C:\Users\User\Desktop\react app\new-react-app\.next\BUILD_ID'

▲ Next.js 14.0.1 Local: http://localhost:3000 [Issue: ENOENT: file not found at 'C:\Users\User\Desktop\react app\new-react-app.next\BUILD_ID'] { errno: -4058, code: 'ENOENT', syscall: 'open& ...

mandatory data fields for an HTML form

I'm having some trouble creating an HTML form with mandatory input fields. The code I have so far is shown below: <div class="col-sm-6"> <label for="city" class="form-control">City ...

Guide to Creating Reusable Components Similar to Bootstrap

Bootstrap libraries like React Bootstrap offer a convenient way to import components, such as: import Modal from '...'; It is possible to destructure Modal further into individual parts: const { Header, Title, Description } = Modal; However, ke ...

"Excessive re-renders caused by the .map function in the webpage

Issue: The tags on the navbar are supposed to remain in place, but when I execute the code, they keep moving - the first tag goes to the second position, the second tag to the third, and this loop continues endlessly. Frontend code (already embedded withi ...

Show a pop-up notification when the quantity in one set of cells matches the quantity in a different set of cells

Does anyone have a script that can show a popup message when the count of yellow cells matches the count of red cells? Here is a visual representation of what I'm looking for. Any assistance would be greatly appreciated. https://i.sstatic.net/2bJsx.p ...

If you encounter the error message "The term 'Office' is not defined no-undef" it may be due to using a version of react-script that is newer than 3.0.0

I encountered an issue while creating an Outlook add-in using React and Typescript. When I tried to run my code with npm run start, I received the following error message, preventing me from running my React app. Failed to compile. ./src/index.tsx Line ...

Guide to changing the color of HTML cells depending on the dynamic text

I'm in the process of developing a game dashboard that not only updates cell statuses but also changes their color accordingly. The backend is powered by a Python script utilizing Flask to serve the page. Main HTML Dashboard <!DOCTYPE html> < ...

Invalid domain used in the HTTP request

Currently, I am facing an issue with a POST request using Axios from my React JS front end to my Node JS backend server. My front end is running on localhost:3000 while my backend is running on localhost:8080. Here is the code snippet from my front end: ...