ReactDOM.render is no longer supported in React 18

ReactDOM.render is no longer supported in React 18 Техника

React’s goal is in many ways to render HTML in a web page.

React renders HTML to the web page by using a function called
createRoot() and its method render().


Quick tutorial on how to add attributes to the <body> element within a React app. There are many use cases for this — the primary being to add a class (e.i. <body class="some--class">, and it works for any other attribute. The reason this is needed is because the <body> lies ‘outside’ most React apps.

The recommended setup for React is to add a <div> within a page’s <body> where your root component will be rendered, like this:

<html>
  <head>
  </head>
  <body>
    <div id="root">
      <!-- Root Component -->
    </div>
  </body>
</html>
Содержание
  1. Add a class
  2. Connecting to a Component
  3. Introduction
  4. What is Create React App?
  5. Why should you use Create React App?
  6. Getting started with Create React App
  7. Installation and folders
  8. node_modules
  9. public
  10. index.html
  11. Title and meta tags
  12. What are bundled scripts?
  13. favicon.ico
  14. package.json
  15. yarn.lock / package-lock.json
  16. Running the React app
  17. How to run tests with the React Testing Library
  18. Writing our app’s stateless components
  19. Styling our React app with CSS
  20. Using Hot Module Replacement in webpack
  21. Apps built using Create React App
  22. Conclusion
  23. Get set up with LogRocket’s modern React error tracking in minutes:
  24. What is React Portals?
  25. What is React portals used for?
  26. Looking at the CSS/HTML solution
  27. React Portal in action
  28. Building a Portal wrapper
  29. Step 1: Adding an extra mount point in a DOM outside of react-root
  30. Step 2: Build a reusable React Portal wrapper component using createPortal in React
  31. Step 3: Passing button coordinates to the tooltip for positioning using React Hooks
  32. Bonus: Prevent tooltips from jumps during page content changes
  33. Using React Hooks with React Portal
  34. Creating our custom React Hook
  35. Conclusion
  36. Get set up with LogRocket’s modern React error tracking in minutes:
  37. ReactDOM
  38. Virtual DOM (VDOM) vs DOM
  39. ReactDOM.render()
  40. Goodbye ReactDOM.render()
  41. ReactDOM.createPortal()
  42. More great articles from LogRocket:
  43. ReactDOM.unmountComponentAtNode()
  44. ReactDOM.findDOMNode()
  45. ReactDOM.hydrate() and Server-Side Rendering (SSR)
  46. Client-Side Rendering (CSR)
  47. Server- Side Rendering (SSR)
  48. Demo of ReactDOM.hydrate()
  49. Changes to hydrate with React 18
  50. Conclusion
  51. Get set up with LogRocket’s modern React error tracking in minutes:
  52. Target container is not a DOM element
  53. # Adding multiple classes to the body element in React
  54. # Set styles on the Body element when an event is triggered
  55. # Add a class to the Body element in React
  56. # Toggle styles on the Body element when an event is triggered
  57. # Pass the correct ID to document. getElementById()
  58. # Toggle a class on the body element in React
  59. # Set styles on the Body element in React
  60. The HTML Code
  61. Example
  62. # Configuring html-webpack-plugin correctly
  63. The Root Node
  64. Example
  65. The createRoot Function
  66. # Place your React script tag at the bottom of the body tag
  67. # Adding a class to the body element when an event occurs
  68. # Table of Contents
  69. # Remove the strict mode wrapper
  70. # React Testing library — ReactDOM. render no longer supported in React 18
  71. # ReactDOM. render is no longer supported in React 18
  72. The render Method
  73. Example

Add a class

There’s nothing fancy here, it’s just plain ole’ Javascript, but in my experience from mentoring students I’ve seen many overlook regular Javascript when diving into React. To access the body from within React was can use document.body, and pair it with any other DOM manipulators. Using this:

document.body.classList.add('modal-open');

would add a modal-open class to the <body>. Similarly,

document.body.classList.remove('modal-open'); 

would remove the modal-open class from the <body>.

Any other DOM manipulation methods can be used with document.body. Here are a few examples:

document.body.id = 'newID';
document.body.style.fontSize = '20px';
document.body.dataset.someDataAttr = 'someValue';

Connecting to a Component

In my case I was implementing a modal into my app. When the modal opens it fills the entire screen with a semi-transparent background, and the modal content box. But scrolling was still enabled on the <body> behind the modal. To prevent this I needed to either:

  1. Add style="overflow: hidden;" to the body, or
  2. Add a class to the body which adds overflow: hidden;

I chose to go with #2, the class method. My modal element is contained within a standalone component within my app. When the modal is open I need to add a class to the <body> to prevent the page from scrolling, then remove the class when the modal is closed. Here’s my stripped down component:

const MODAL_OPEN_CLASS = "body--modal-open";

class Modal extends Component {
  componentDidMount() {
    document.body.classList.add(MODAL_OPEN_CLASS);
  }

  componentWillUnmount() {
    document.body.classList.remove(MODAL_OPEN_CLASS);
  }

  render() {
    return (
      <div className="modal">
        <div className="modal__content"></div> 
      </div>
    )
  }
}

Now, when the Modal component is mounted the <body> has the body--modal-open classes added, and when the Modal component is unmounted the class is removed. And with some basic CSS targeting the .body--modal-open class I can prevent the page from scrolling while the modal is open.


Create React App is a popular toolchain for building simple React apps. The goal of this post is to explain the components of Create React App at a deeper level to give you much better control and confidence over the development environment.

  • Introduction
  • What is Create React App?
  • Getting started with Create React App
  • What are bundled scripts?
  • Running the React app
  • Styling our React app with CSS
  • Using Hot Module Replacement in webpack
  • Apps built using Create React App

Introduction

Before we understand what Create React App solves, let’s first learn what a toolchain is. In simple terms, a toolchain is a set of distinct software development tools linked by specific stages. In other words, any software development framework is made up of a bunch of supporting tools optimized to do specific functions. For instance, in C++ development, we need a compiler to compile the code and a build system like CMake to manage all the dependencies if the project is fairly big. In this case, the compiler and CMake become part of the toolchain.

In React development, different toolchains satisfy different requirements for product development. For instance, Next.js is great for building a server-rendered website, and Gatsby is optimized for static, content-oriented websites like blogs and newsletters.

What is Create React App?

Create React App is also a toolchain. It is specifically recommended by the React community for building single-page applications (SPAs) and for learning React (for building “Hello, World!” applications). It sets up your development environment so that you can use the latest JavaScript features, provides a nice developer experience, and optimizes your app for production.

Create React App was created by Joe Haddad and Dan Abramov. The GitHub repository is very active and maintained by the creators, along with many open source developers from different parts of the world. If you’re interested in contributing, the repository’s contributing page is an excellent place to start.

Why should you use Create React App?

  • It abstracts away the complex configurations that come with creating a new React project
  • It comes with a built-in development server that allows you to see changes in real time as you make them
  • It includes a comprehensive set of development tools, such as linting and testing, out-of-the-box
  • It is actively maintained and regularly updated with new features and bug fixes
Дополнительно:  Root explorer скачать на андроид на русском бесплатно последнюю версию без вирусов бесплатно андроид

Getting started with Create React App

Installation and folders

~ npx create-react-app rate-restaurants

This command runs for a few seconds and exits happily after creating a bare-bones React application under a new directory called rate-restaurants. Now, cd into the rate-restaurants directory. The directory looks something like this:

Create React App Directory Structure
Create React App Directory Structure

node_modules

This folder is part of the npm system. npm puts local installs of packages in ./node_modules of the current package root. Basically, the packages you want to use by calling an import statement go here.

public

This folder contains the index.html and manifest.json files. Let’s look at the files inside the public folder.

index.html

This index.html serves as a template for generating build/index.html, which is ultimately the main file served on the browser. Let’s take a look at this file’s contents:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" data-hren="%PUBLIC_URL%/favicon.ico" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <meta name="theme-color" content="#000000" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" data-hren="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

Title and meta tags

The <meta> tags provide metadata about the HTML document. They describe the content of the page. <meta> tags usually aren’t displayed on the webpage but are machine parsable. The bundled scripts are finally placed into the <body> title of this HTML file.

What are bundled scripts?

In order to understand this, we need to learn about one more concept in the world of modern JavaScript toolchains, which is webpack. Think of webpack as a tool that bundles up all your source files and creates a single bundle.js file that can be served from the index.html file inside a <script> tag.

This way, the number of HTTP requests made within the app is significantly reduced, which directly improves the app’s performance on the network. Besides, webpack also helps in making the code modular and flexible when you supply it with additional config options. Here’s a visual of it:

Webpack Diagram
Webpack Diagram

The above figure shows an example recipe app built using React and bundled using webpack. Webpack has a webpack.config.js file, which is used to specify the configuration settings. It typically looks something like this:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
};

The entry key specifies the entry point for webpack, and output specifies the location where the bundle.js file will be stored after the build process.

Coming back to index.html, Create React App uses html-webpack-plugin for bundling. If you look at the webpack.config.js here, the entry key points to src/index.js, which specifies the entry point for webpack. When webpack compiles the assets, it produces a single bundle (or several, if you use code splitting). It makes their final paths available to all plugins  —  one such plugin is for injecting scripts into HTML.

The html-webpack-plugin is also enabled to generate the HTML file. In Create React App’s webpack.config.js, it is specified that it should read public/index.html as a template. The inject option is also set to true. With that option, html-webpack-plugin adds a <script> with the path provided by webpack right into the final HTML page.

This final page is the one you get in build/index.html after running npm run build, and the one that gets served from / when you run npm start. Now that we understand index.html, let’s move on to manifest.json.

This is a web app manifest that describes your application, and it’s used by mobile phones if a shortcut is added to the home screen. Let’s look at the contents to understand it further:

{
  "short_name": "React App",
  "name": "Create React App Sample",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    }
  ],
  "start_url": ".",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}

favicon.ico

This is simply the icon image file used for our application. You can see this linked inside index.html and manifest.json. Before moving on to the src/directory, let’s look at a couple other files on our root.

package.json

The scripts field is of particular interest here. You can see that the start, build, test, and eject commands point to react-scripts’ version of start, build, test, and eject. This specifies that when you run npm commands like npm start, it will actually run react-scripts start.

react-scripts is a set of scripts from the Create React App starter pack. react-scripts start sets up the development environment and starts a server, as well as convenient development features such as hot module reloading. You can read here to see everything it does for you.

yarn.lock / package-lock.json

Create React App will detect if you have Yarn installed and if so will use that to install dependencies rather than npm. If you don’t have Yarn installed, npm will be used and you will see a package-lock.json file rather than the yarn.lock file.

In order to get consistent installs across machines, Yarn needs more information than the dependencies you configure in your package.json. Yarn needs to store exactly which versions of each dependency were installed. That’s what the yarn.lock file (or package-lock.json file if npm is used) in the root of your project is for.

Running the React app

Now, let’s fire up the application. To run the app, you can either run npm start or yarn start. Once you run the command, open http://localhost:3000 to view the application.

Note that npm run build or yarn build will build the app for production and store it inside the build/ folder, which can be deployed to production. For the sake of this tutorial, let’s use npm start for development instead of building the app for production. Your app should look like this:

Дополнительно:  Не включается черный экран ноутбука - Топ 5 причин - Мастер Пайки

Create React App Home Screen
Create React App Home Screen

Let’s try and understand what the entry point is for this app. When we looked at webpack, I mentioned that webpack’s entry is index.js, which is the entry point for the React application.

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

This line calls ReactDOM’s render() method, which renders a React element into the DOM in the supplied container and returns a reference to the component. The React element here is the <App> component, and the supplied container is the DOM element root (which is referenced in index.html).

<App> is the root component of this app. Let’s look at App.js, where it is defined:

import logo from './logo.svg';
import './App.css';

function App () {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit src/App.js and save to reload. </p> <a className="App-link" data-hren="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } export default App;

The App component is a function for defining a React component. This is the code that we are interested in. Any application can be built by stripping stuff out and tweaking the App.js. We can even build React components separately inside a src/components/folder and import these components inside App.js. A typical React application looks something like this:

Diagram of a React App
Diagram of a React App

There is a root component, <App>, that imports other child components, which in turn import other child components. Data flows from root to children through React properties (called props) and flows back up using callback functions. This is the design pattern used by any basic React application.

At this point, we should be able to start building any simple single-page application by tweaking App.js and adding the necessary components. The App.css file can be used for styling the application.

How to run tests with the React Testing Library

React Testing Library is a testing utility for React that provides a simple and intuitive way to test React components. It is built on top of the DOM Testing Library, which provides a set of utilities for testing DOM nodes in a browser-like environment.

Instead of concentrating on implementation details, React Testing Library encourages developers to build tests that mimic how consumers interact with the application. As a result, tests created with the React Testing Library ought to be more reliable and less prone to fail when a component’s implementation is changed.

import { render, screen } from '@testing-library/react';

We import the file that we want to test in the next line, as shown below:

import App from './App';

The next step is to write out the cases that we want to test for and then check if the component works as expected:

// The imports remain the same 

test('renders learn react link', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

Create React App Testing Part One
Create React App Testing Part One

We can check if this is working by intentionally failing out test and then running yarn test again:

Create React App Testing Part Two
Create React App Testing Part Two

Our final app will look something like this:

Final Product of Create React App
Final Product of Create React App

Writing our app’s stateless components

The first step is to write our app’s stateless components, which we’ll need to build the cards with the data and rating system in them. In order to do this, we create a folder called components/inside src/and add a file called Card.js:

import React from 'react';

const CardBox = (props) => {
  return <div className="card-body">{props.children}</div>;
};
const Image = (props) => {
  return <img src={props.image} alt="Logo" className="picture" />;
};
const Name = (props) => {
  return <div className="name">{props.name}</div>;
};
const Details = (props) => {
  return <div className="details">{props.details}</div>;
};
const Star = ({ selected = false, onClick = (f) => f }) => (
  <div className={selected ? 'star selected' : 'star'} onClick={onClick}></div>
);
const Card = (props) => {
  return (
    <CardBox>
      <div className="inner-body">
        <Image image={props.image} />
        <div className="body">
          <div className="inner-body">
            <Name name={props.name} />
          </div>
          <Details details={props.details} />
          <div className="inner-body">
            {[...Array(5)].map((n, i) => (
              <Star
                key={i}
                selected={i < props.starsSelected}
                onClick={() => props.change(props.id, i + 1)}
              />
            ))}
          </div>
        </div>
      </div>
    </CardBox>
  );
};
export default Card;

As you can see, we are creating a separate stateless component for each element inside the card — namely, the restaurant name, details, image, and the rating section. Then, we wrap all of this inside a Card component and export it as a default:

import { useState } from 'react';
import './App.css';
import Card from './components/Card';
import { v4 as uuidv4 } from 'uuid';

function App() {
  const [restaurants, setRestaurants] = useState([
    {
      id: uuidv4(),
      name: 'Sushi S',
      details: '2301 Moscrop Street, Burnaby, BC V61 23Y',
      image:
        'https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80',
      starsSelected: 0,
    },
    {
      id: uuidv4(),
      name: 'Agra Tandoori',
      details: '1255 Canada Way, Burnaby, BC V61 23Y',
      image:
        'https://images.unsplash.com/photo-1552566626-52f8b828add9?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80',
      starsSelected: 0,
    },
    {
      id: uuidv4(),
      name: 'Bandidas Taqueria',
      details: '2544 Sanders Avenue, Richmond, BC V6Y 0B5',
      image:
        'https://images.unsplash.com/photo-1537047902294-62a40c20a6ae?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=870&q=80',
      starsSelected: 0,
    },
  ]);
  const onChange = (id, starsSelected) => {
    setRestaurants(
      [...restaurants].map((restaurant) => {
        if (restaurant.id === id) {
          restaurant.starsSelected = starsSelected;
        }
        return restaurant;
      })
    );
  };

  return (
    <div className="main-body">
      {[...restaurants].map((restaurant, index) => (
        <Card
          key={index}
          name={restaurant.name}
          details={restaurant.details}
          image={restaurant.image}
          starsSelected={restaurant.starsSelected}
          id={restaurant.id}
          change={(id, starsSelected) => onChange(id, starsSelected)}
        />
      ))}
    </div>
  );
}
export default App;

In the App.js, we import Card. The restaurant data is modeled as the state of this app. Saving the state separately in a file outside of App.js is a better design as the app and its restaurant data grow. In the return() function, we pass this data to the Card component as props.

Data flows down to child components as properties and flows back up through callbacks, the OnChange callback used for updating the star ratings. That’s it! Now, when you go to http://localhost:3000/, you should see the rate restaurant app, ready to go.

Styling our React app with CSS

We are done building the functionality of the application. The next step is how to go about styling the application and making it look exactly how we want. Create React App makes it easier for us to use CSS as it comes with inbuilt CSS support. All we need to do is copy the code below and paste it into the App.css file:

// App.css

.main-body {
  display: flex;
  flex-direction: column;
  background-color: #11171d;
  min-height: 80rem;
}
.body {
  display: flex;
  flex-direction: column; 
}
.inner-body {
  display: flex;
  flex-direction: row;
}
.card-body {
  display: flex;
  flex-direction: column;
  width: 566px;
  height: 170px;
  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5);
  background-color: #15202a;
  margin-left: 25rem;
  margin-bottom: 0.1rem;
}
.card-body:hover { 
  background-color: #11171d;
  border: #ffffff;
}
.picture {
  margin-left: 24px;
  margin-top: 17px;
  border-radius: 50%;
  width: 48px;
  height: 48px;
}
.name {
  display: flex;
  flex-direction: row;
  margin-left: 15px;
  margin-top: 19px;
  min-width: 85px;
  height: 17px;
  font-family: HelveticaNeue;
  font-size: 14px;
  font-weight: 500;
  font-style: normal;
  font-stretch: normal;
  line-height: normal;
  letter-spacing: -0.6px;
  color: #ffffff;
}
.details {
  margin-left: 15px;
  margin-top: 7px;
  width: 445px;
  height: 88px;
  font-family: HelveticaNeue;
  font-size: 14px;
  font-weight: 300;
  font-style: normal;
  font-stretch: normal;
  line-height: normal;
  letter-spacing: -0.6px;
  color: #ffffff;
}
.star {
  cursor: pointer;
  height: 25px;
  width: 25px;
  margin-left: 0px;
  float: left;
  background-color: grey;
  clip-path: polygon(
  50% 0%,
  63% 38%,
  100% 38%,
  69% 59%,
  82% 100%,
  50% 75%,
  18% 100%,
  31% 59%,
  0% 38%,
  37% 38%
  );
 }
 .star.selected {
  background-color: red;
 }

With that done, we see our tastefully styled application when you navigate to the browser. You can, however, choose to use more advanced CSS configurations like Sass and/or CSS frameworks like Tailwind CSS or styled-components in your application.

Дополнительно:  50 Latin Roots That Will Help You Understand the English Language

Using Hot Module Replacement in webpack

Hot Module Replacement (HMR) is a feature in webpack to inject updated modules into the active runtime. It’s like LiveReload for every module. HMR is opt-in, so you need to input some code at chosen points of your application. The dependencies are handled by the module system.

// regular imports
ReactDOM.render(<App /> , document.getElementById('root'))

if (module.hot) {
  module.hot.accept('./App', () => {
    ReactDOM.render(<App />, document.getElementById('root'))
  })
}

Apps built using Create React App

Finally, for some motivation to use Create React App for building apps, let’s look at some of the well-known products bootstrapped and built using Create React App. CRA provides a development server, builds scripts, and many other useful features out-of-the-box, which helps developers to focus on writing code and building UI rather than setting up the development environment. Popular apps created with CRA include:

  • Facebook: The social media behemoth used CRA to create its Messenger application. React, Redux, and CRA were used to develop the instant messaging and video calling platform, Messenger. The software is accessible via several platforms, including the web, Android, and iOS
  • Dropbox: Dropbox, the cloud storage and file hosting service, uses CRA for building its web application. The web application allows users to upload, store, and share files online. The app is built using React, Redux, and CRA, and it is available on multiple platforms, including Windows, macOS, and Linux
  • Slack: Slack, the collaboration hub for teams, uses CRA for building its web application. The web application is used by teams to communicate, share files, and collaborate on projects. The app is built using React, Redux, and CRA, and it is available on multiple platforms, including Windows, macOS, and Linux

This GitHub comments section also has a long list of production-ready apps built using Create React App. A couple of the ideas are even part of Y-Combinator.

Conclusion

I hope you now understand the different elements of Create React App better. What are you waiting for? Fire that terminal up, install Create React App, and start building your awesome ideas!

Get set up with LogRocket’s modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.
  3.      


     to your HTML


  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • ngrx middleware
    • Vuex plugin

Get started now

In this article, we’ll look at a real-world example of React Portals and explain how it can help solve the overflow: a hidden problem on a tooltip example:

React Portal Example
React Portal Example

  • What is React Portals?
  • What is React Portals used for?
  • Looking at the CSS/HTML solution 
  • React Portal in action
  • Building a React Portal wrapper
  • Using React Hooks with React Portal

What is React Portals?

React Portals are an advanced concept that allows developers to render their elements outside the React hierarchy tree without comprising the parent-child relationship between components.

Usually, typical React components are located within the DOM. This means that it might be tricky for you to render modals or pop-up windows.

export default function App() {
  const [visibility, setVisibility] = useState("hidden");
  const handleClick = () => {
    if (visibility === "hidden") {
      //if the visibility state is 'hidden'
      setVisibility("visible"); //then set it to 'visible'
    } else {
      setVisibility("hidden"); //otherwise, make it 'hidden'
    }
  };
  return (
    <div className="App">
      <button onClick={() => handleClick()}>Show/Hide</button>
      {/*The visibility of the modal*/}
      <div style={{ visibility }}>
        <p>This is a modal</p>
      </div>
    </div>
  );
}

React Portal Modal Example GIF

As you can see, our code works. However, this doesn’t look like a modal window at all. We want our component to pop out rather than blend in with the rest of the webpage.

This is a widespread problem that arises all the time in web development: you want to build some tooltip or dropdown, but it’s cut by the parent element overflow: hidden styling:

React Portal Hidden Tooltip
React Portal Hidden Tooltip

In the screenshot above, the parent container with the overflow:hidden style is marked with red, and the element used for positioning is marked with green. Because of this style property, rendering modal windows won’t be an easy task. So how do we solve this problem?

This is where React Portals comes in. This concept allows developers to render an element outside its parent’s DOM node. Despite this, React still preserves the component’s position in the component hierarchy.

What is React portals used for?

Portals are great for places where you want to render elements on top of each other. I’ve included some common examples below:

  • Profile cards: Provides quick information about the user’s profile without clicking on their page:

React Portal Profile Card Information GIF

  • Loading screens: When a task is going on in the background(for example, a fetch request), it’s sensible to show a loading screen. As a result, this prevents the user from interacting with the app
  • Cookie alerts: Provides the user with options to let them choose what cookies they want to allow in their web browser:

React Portal Cookie Example
React Portal Cookie Example

return (
  <div className="App">
    {/*Display within DOM node*/}
    <div className="exists">
      <p>This is within the DOM node</p>
    </div>
  </div>
);

React Portal Elements With DOM
React Portal Elements With DOM

As you can see, our component is showing up within the root div element. However, when it comes to modals and dialog windows, we want our component to render out of the root component.

We can solve this by creating a React Portal element:

function App() {
  return (
    <div className="App">
      {/*Render a modal*/}
      <MyModal />
    </div>
  );
}
//create a modal
function MyModal() {
  //to create a portal, use the createPortal function:
  return ReactDOM.createPortal(
    <div className="modal">
      <p>This is part of the modal</p>
    </div>,
    document.body
  );
}

This time, notice that React will not render this function within the DOM:

React Portal Modal DOM Example
React Portal Modal DOM Example

Looking at the CSS/HTML solution

The simplest way to solve this issue is by simply removing the overflow styling:

div {
  /*'Hidden' hides the element*/
  overflow: hidden; /* remove this line of code */
  /*When 'overflow' is set to 'visible', CSS will render this to the UI*/
  /* Further styles.. */
} 

This tells the program to modal our modal onto the screen:

React Portal With No Padding
React Portal With No Padding

  1. Somebody could accidentally add overflow: hidden to the parent again (and forget to click your button with tooltip for testing!)
  2. Somebody could add another parent wrapper around it, for example, to introduce some extra styling in some cases
  3. There is also the possibility that overflow: hidden was there for a reason, for example, to crop an image

Here’s an example of an unwanted side effect of disabling overflow: hidden:

ReactDOM.render is no longer supported in React 18Before image is inside the bounds of the card

» data-medium-file=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-styling.png?w=300″ data-large-file=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-styling.png?w=730″ decoding=»async» loading=»lazy» src=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-styling.png?is-pending-load=1″ alt=»React Portal Parent Styling Example» srcset=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-styling.png 730w, https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-styling.png?resize=300,85 300w» sizes=»(max-width: 730px) 100vw, 730px» data-lazy-srcset=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-styling.png 730w, https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-styling.png?resize=300,85 300w» data-old-srcset=»data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7″>

Before image is inside the bounds of the card

After (image has expanded far outside of the card marked with green):

ReactDOM.render is no longer supported in React 18After image has expanded far outside of the card marked with green

» data-image-caption=»

After image has expanded far outside of the card marked with green

» data-medium-file=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-green-square.png?w=300″ data-large-file=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-green-square.png?w=730″ decoding=»async» loading=»lazy» src=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-green-square.png?is-pending-load=1″ alt=»React Portal Green Square Example» srcset=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-green-square.png 730w, https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-green-square.png?resize=300,130 300w» sizes=»(max-width: 730px) 100vw, 730px» data-lazy-srcset=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-green-square.png 730w, https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-green-square.png?resize=300,130 300w» data-old-srcset=»data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7″>

After image has expanded far outside of the card marked with green

React Portal in action

There’s a way to solve all the problems with tooltip/dropdown cut off by overflow for the entire application and reuse the code without needing to spend developer time on trying and testing.

The solution is to append tooltip or dropdown directly to the body of the document, set position: fixed style, and provide screenX and screenY coordinates where the tooltip/dropdown should appear.

Now, there are two things we need to do:

  1. Append the tooltip/dropdown to the body of the document outside of the React mount root
  2. Take coordinates for placing the tooltip/dropdown (for example, using useRef React Hook)

Let’s start with mounting outside of React. That’s an easy task for a JQuery/Vanilla JS codebase. Still, it might sound challenging to a React developer because React applications usually have only one mount point to the DOM. For example, some div with id = "root".

Luckily, the React team introduced another way to mount components: React Portal. Using React Portal, developers can access the tooltip/dropdown component from JSX in a convenient way: all of the props pass and handle events, but at the same time, Portal is mounted to the body of the document outside of the React mount root.

<Portal>
           <TooltipPopover coords={coords}>
              Awesome content that will never be cut off again!
           </TooltipPopover>
        </Portal>

React Portal Content Wrapper
React Portal Content Wrapper

And that’s it! You have a universal solution for any content that should pop up outside of the parent without being cut off. But the <Portal/> wrapper component is a “black box” for us, so let’s change that and look at what’s under the hood.

Building a Portal wrapper

Step 1: Adding an extra mount point in a DOM outside of react-root

<html>
    <body>
        <div id="react-root"></div> // [ 1 ]
        <div id="portal-root"></div>
    </body>
</html>

In this code snippet, I have named the React mount point element ID "react-root", and all of the tooltips/dropdowns should be mounted using React Portal inside of "portal-root".

Step 2: Build a reusable React Portal wrapper component using createPortal in React

Here is a simplified <Portal/> wrapper component code written with React Hooks:

import { useEffect } from "react";
import { createPortal } from "react-dom";

const Portal = ({children}) => {
  const mount = document.getElementById("portal-root");
  const el = document.createElement("div");

  useEffect(() => {
    mount.appendChild(el);
    return () => mount.removeChild(el);
  }, [el, mount]);

  return createPortal(children, el)
};

export default Portal;

As you can see, mount needs a DOM element with id = "portal-root" from the previous code snippet with HTML to append an element inside. The core thing this wrapper component does is create a Portal for any React children passed into a component.

The useEffect React Hook is used here to take care of mounting the element at the right time and to clean up on component unmount.

Step 3: Passing button coordinates to the tooltip for positioning using React Hooks

The last thing we need to do to get the fully-functional tooltip component is pass button coordinates to the tooltip for positioning. That is not a hard task, thanks to React Hooks, and it can be implemented with something like this:

const App = () => {
  const [coords, setCoords] = useState({}); // takes current button coordinates
  const [isOn, setOn] = useState(false); // toggles button visibility

  return <Card style={{...styles.card, overflow: "hidden"}}> // [ 2 ]
      <Button
        onClick={e => {
          const rect = e.target.getBoundingClientRect();
          setCoords({
            left: rect.x + rect.width / 2,
            top: rect.y + window.scrollY
          });
          setOn(!isOn); // [ 3 ]
        }}
      >
        Click me
      </Button>
      {
        isOn &&
        <Portal>
          <TooltipPopover coords={coords}>
            <div>Awesome content that is never cut off by its parent container!</div>
          </TooltipPopover>
        </Portal>
      }
  </Card>
}

In this code, the button component has an onClick event handler that takes current on-screen coordinates of the button from an e.target object using the standard getBoundingClientRect() method of a DOM element.

Additionally, a toggler for button visibility is in place that helps us to toggle the tooltip.

Please note that I intentionally left on the Card component to showcase that the Portal solution is working fine.

Feel free to check the live demo and full code on codesandbox.

Bonus: Prevent tooltips from jumps during page content changes

There is one thing that refers to the tooltip positioning more than to Portals, but it’s worth mentioning. If the button position depends on the window’s right edge (for example, display: flex; margin-left: auto styling), its positioning could be affected by the window scroll appearing (for example, when new content is loaded at the bottom of the page). Let’s take a look at an example:

ReactDOM.render is no longer supported in React 18Before: The window has no scroll, and the tooltip is centered relative to the button

» data-medium-file=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-no-scroll.png?w=300″ data-large-file=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-no-scroll.png?w=730″ decoding=»async» loading=»lazy» src=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-no-scroll.png?is-pending-load=1″ alt=»React Portal No Scroll Example» srcset=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-no-scroll.png 730w, https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-no-scroll.png?resize=300,114 300w» sizes=»(max-width: 730px) 100vw, 730px» data-lazy-srcset=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-no-scroll.png 730w, https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-no-scroll.png?resize=300,114 300w» data-old-srcset=»data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7″>

Before: The window has no scroll, and the tooltip is centered relative to the button
ReactDOM.render is no longer supported in React 18After: The window scroll has appeared, and the tooltip is a bit off-center (exactly the same amount of pixels as the scroll added)

» data-medium-file=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-company-styling.png?w=300″ data-large-file=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-company-styling.png?w=730″ decoding=»async» loading=»lazy» src=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-company-styling.png?is-pending-load=1″ alt=»React Portal Parent Company Styling Example» srcset=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-company-styling.png 730w, https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-company-styling.png?resize=300,108 300w» sizes=»(max-width: 730px) 100vw, 730px» data-lazy-srcset=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-company-styling.png 730w, https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-parent-company-styling.png?resize=300,108 300w» data-old-srcset=»data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7″>

After: The window scroll has appeared, and the tooltip is a bit off-center (exactly the same amount of pixels as the scroll added)

There are a few ways to solve this issue. You could use some resize detection package applied to the whole page, like react-resize-detector, which will fire some event on content height change.

Then, we can measure the scroll width and correct the tooltip’s position.

Luckily, in our case, there is a much simpler pure CSS solution:

html {
    overflow-x: hidden;
    width: 100vw;
}

Adding this little code snippet to the page prevents the page’s content from unexpected jumps on window scroll appear/hide because the <html/> width is set to be equal to 100vw (window width), which is constant and unaffected by the window scroll.

Meanwhile, the 100% <html/> width doesn’t include the scroll, so the app no longer cares about the scroll being on or off. Tooltip will be centered all the time.

You can test the result on the demo by playing with the window height size. Doing the same thing but with better-looking cross-browser scrollbars is also possible using a package called react-custom-scrollbars.

To make it work, you need to install the package and wrap the whole app into a Scrollbars component like this:

import { Scrollbars } from 'react-custom-scrollbars';

ReactDOM.render(
  <Scrollbars style={{ width: "100vw", height: "100vh" }}>
    <App />
  </Scrollbars>, 
  document.getElementById("react-root")
);

Here is a quick preview (note the scrollbar appearance):

ReactDOM.render is no longer supported in React 18Note: The Scrollbar appearance

» data-medium-file=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-content-scrollbar.png?w=300″ data-large-file=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-content-scrollbar.png?w=730″ decoding=»async» loading=»lazy» src=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-content-scrollbar.png?is-pending-load=1″ alt=»React Portal Content Scrollbar» srcset=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-content-scrollbar.png 730w, https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-content-scrollbar.png?resize=300,103 300w» sizes=»(max-width: 730px) 100vw, 730px» data-lazy-srcset=»https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-content-scrollbar.png 730w, https://blog.logrocket.com/wp-content/uploads/2019/12/react-portal-content-scrollbar.png?resize=300,103 300w» data-old-srcset=»data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7″>

Note: The Scrollbar appearance

Using React Hooks with React Portal

In this section, we will dive deeper into combining React Hooks with Portals. React Hooks are great when you want to write reusable code. This means we can create a custom Hook that would allow us to render our modal relatively easily. Consequently, this brings cleaner, more readable, and robust code to the table.

Creating our custom React Hook

We can even create a custom React Hook to allow us to build a Portal:

//file name: usePortal.js
// The complete breakdown of this code is in the comments
import { useState, useCallback, useEffect } from "react";
import ReactDOM from "react-dom";
const usePortal = (el) => {
  const [portal, setPortal] = useState({
    render: () => null,
    remove: () => null,
  });

  const createPortal = useCallback((el) => {
    //render a portal at the given DOM node:
    const Portal = ({ children }) => ReactDOM.createPortal(children, el);
    //delete the portal from memory:
    const remove = () => ReactDOM.unmountComponentAtNode(el);
    return { render: Portal, remove };
  }, []);

  useEffect(() => {
    //if there is an existing portal, remove the new instance.
    //is prevents memory leaks
    if (el) portal.remove();
    //otherwise, create a new portal and render it
    const newPortal = createPortal(el);
    setPortal(newPortal);
    //when the user exits the page, delete the portal from memory.
    return () => newPortal.remove(el);
  }, [el]);

  return portal.render;
};
export default usePortal; //link this Hook with the project
//import our custom Hook into the project.
import usePortal from "./usePortal";
function App() {
  //render our portal within the element which has the class of 'me'
  const Portal = usePortal(document.querySelector(".me"));
  return (
    <div className="App">
      <Portal>Hi, world</Portal>
    </div>
  );
}

This will be the outcome:

React Portal with React Hooks
React Portal with React Hooks

Notice that React didn’t render our Portal. This is because, in our code, we told the compiler to draw our component into a node named me. However, in our index.html file, we haven’t created a me element.

To fix this, add this line of code to index.html:

<!-- Create a div with a className of 'me'. Our portal will be rendered here --->
<div class="me"></div>
<!-- Further code.... -->

React Portal with React Hooks Complete
React Portal with React Hooks Complete

Conclusion

We have gone through the most common use case for React Portal step by step, explaining how it works on a real-life example with tooltip component development from scratch. Furthermore, we also covered using React Hooks and Portals to help us build custom hooks.

Of course, generalization can’t come without its tradeoffs. The complexity of Portal tooltip is bigger than the pure CSS/HTML solution, and it’s up to the developer to choose the right approach at the right time.

Get set up with LogRocket’s modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.
  3.      


     to your HTML


  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • ngrx middleware
    • Vuex plugin

Get started now

Whenever you create a React project, one of the first things you need to install along with the react package is the react-dom package. Have you ever wondered why you need it?

You can check out the code for the sample app on Github and the deployed site.

ReactDOM

ReactDOM renders components or JSX elements to the DOM. The ReactDOM object has only a handful of methods; you’ve probably used the render() method, which is responsible for rendering the app in the browser.

The react-dom package serves as the entry point to the DOM. We import it at the top of our project like this:

import ReactDOM from 'react-dom';

Before we go into the details of ReactDOM’s methods, let’s first understand why we need ReactDOM instead of just using the DOM.

Virtual DOM (VDOM) vs DOM

JavaScript and HTML can’t communicate directly with one another, so the DOM was developed to deal with this issue. When you open a webpage, the browser engine translates all of its content into a format that JavaScript can understand — a DOM tree.

The structure of this tree is identical to that of the corresponding HTML document. If one element is nested inside another in the HTML code, this will be reflected in the DOM tree.

You can see for yourself what the DOM looks like by opening the Elements tab in the developer tools panel of your browser. What you’ll see will look very similar to HTML code, except that rather than looking at HTML tags, what you’re actually seeing is elements of the DOM tree.

Let’s see DOM in action. The diagram shows how the DOM sees the HTML:

<body>
   <nav> 
       <ul>
          <li>Home</li>
          <li>Contact</li>
      </ul>
  </nav>
    <section class="cards">
         <img src="" alt="" />
            <div class="post-content">
                <h1>Virtual DOM vs DOM</h4> 
            </div>
    </section>
</body>

Diagram of DOM tree structure
Diagram of DOM tree structure

Whenever a browser detects a change to the DOM, it repaints the entire page using the new version. But do we really need to repaint the whole page? Comparing two DOMs to determine which parts have changed is very time consuming.

A Virtual DOM is when React creates their own representation of the DOM as a JavaScript object. Whenever a change is made to the DOM, the library instead makes a copy of this JavaScript object, makes the change to that copy, and compares the two JavaScript objects to see what has changed. It then informs the browser of these changes and only those parts of the DOM are repainted.

Making changes to JavaScript objects and comparing them is far faster than trying to do the same with DOMs. Since this copy of the DOM is stored in memory as a JavaScript object it’s called a Virtual DOM.

The Virtual DOM prevents unnecessary repaints by only repainting updated elements and groups. The VDOM is a lightweight, fast, in-memory representation of the actual DOM.

Even though React works with the VDOM whenever possible, it will still regularly interact with the actual DOM. The process by which React updates the actual DOM to be consistent with the VDOM is called reconciliation.

ReactDOM.render()

ReactDOM.render(element, container[, callback])
ReactDOM.render(<h1>ReactDOM</h1>, document.getElementById("app"))

The first argument is the element or component we want to render, and the second argument is the HTML element (the target node) to which we want to append it.

Generally, when we create our project with create-react-app, it gives us a div with the id of a root inside index.html, and we wrap our React application inside this root div.

So, when we use the ReactDOM.render() method, we pass in our component for the first argument, and refer to id="root" with document.getElementById("root") as the second argument:

<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- ... -->
  </head>
  <body>
   <!-- ... -->
    <div id="root"></div>
  </body>
</html>
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';

// create App component
const App = () => {
   return <div>Render Me!</div>
}

// render App component and show it on screen
ReactDOM.render(<App />, document.getElementById('root'));

Goodbye ReactDOM.render()

In June, the React team announced React 18, and with the new update, we will not be using ReactDOM.render() anymore. Instead, we will be using ReactDOM.createRoot.

The alpha version of React 18 is available, but it will take several months for the public beta. If you want to experiment with the React 18 alpha version, install it like this:

npm install [email protected] [email protected]

With React 18, we will use ReactDOM.createRoot to create a root, and then pass the root to the render function. When you switch to createRoot, you’ll get all of the new features of React 18 by default:

import ReactDOM from "react-dom";
import App from "App";

const container = document.getElementById("app");
const root = ReactDOM.createRoot(container);

root.render(<App />);

ReactDOM.createPortal()

Our second method on ReactDOM is createPortal.


More great articles from LogRocket:


Do you ever need to create an overlay, or modal? React has different functions to deal with modals, tooltips, or other features of that nature. One is the ReactDOM.createPortal() function.

To render a modal, or overlay, we need to use the z-index property to manage the order in which elements appear on the screen. z-index allows us to position elements in terms of depth, along the z-axis.

However, as you know, we can only render one div, with all the other elements nested inside our root div. With the help of the createPortal function, we can render our modals outside of the main component tree. The modal will be the child of the body element. Let’s see how.

In our index.html we will add another div for the modal:

// index.html
<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <div id="modal"></div>
 </body>

The ReactDOM.createPortal() function takes two arguments: the first is JSX, or what we want to render on the screen, and the second argument is a reference to the element we want to attach to our modal:

// Modal.js

import { createPortal } from 'react-dom';
const modalRoot = document.querySelector('#modal');

const Modal = ({ children }) => createPortal(children, modalRoot);

export default Modal;

Now, to render our component, we can pass whatever we want to show between our modal component’s opening and closing tags. This will be rendered inside the modal component as children. I have rendered the modal inside App.js.

// App.js

import React, { useCallback, useState } from 'react';
import Modal from '../Modal';

const App = () => {
  const [showModal, setShowModal] = useState(false);

  const openModal = useCallback(() => setShowModal(true), []);
  const closeModal = useCallback(() => setShowModal(false), []);

  return (
    <div className='App'>
      <button onClick={openModal} className='button node'>
        Click To See Modal
      </button>
      {showModal ? (
        <Modal>
          <div className='modal-container'>
            <div class='modal'>
              <h1>I'm a modal!</h1>
              <button onClick={closeModal} className='close-button'></button>
            </div>
          </div>
        </Modal>
      ) : null}
    </div>
  );
};

export default App;

Gif of a modal popping up after clicking a button

ReactDOM.unmountComponentAtNode()

We use this method when we need to remove a DOM node after it’s mounted, and clean up its event handlers and state.

We will continue on with our code again, this time we will unmount our root div:

ReactDOM.unmountComponentAtNode(container)

If you try to unmount the modal id, you will see an error. This is because the modal is not mounted, so it will return false:

// App.js

const App = () => {
  ReactDOM.unmountComponentAtNode(document.getElementById("root"));

  return (
    <button onClick={handleUnmount} className='button'>
      Unmount App
    </button>
  )
}

This code is enough to unmount the root.

Gif of modal appearing at button click with "unmount app" button

ReactDOM.findDOMNode()

We know that we can render our DOM elements with the render method. We can also get access to the underlying DOM node with the help of findDOMNode method. According to the React documentation, this method is discouraged because it pierces the component abstraction.

NOTE: findDOMNode method has been deprecated in StrictMode.

Another key point is the Node element that you want to access must be mounted, which means it must be in the DOM. If it’s not mounted, findDOMNode returns null. Once you get access to the mounted DOM node, you can use the familiar DOM API to inspect the node.

findDOMNode takes one parameter, and it’s the component:

ReactDOM.findDOMNode(component)

We will continue with the code that we used on the createPortal method. I created a new button and added the text Find The Modal Button and Change its Background Color, and added an onClick handler function. With this function, I accessed its className with the document.querySelector method and changed its background color to black:

const App = () => {
  const handleFindDOMNode = () => {
    const node = document.querySelector('.node');
    ReactDOM.findDOMNode(node).style.backgroundColor = 'black';
  };

 return (
  <button onClick={handleFindDOMNode} className='button'>
    Find The Modal Button and Change its Background Color
  </button>
  )
}

Same gif as before, but with an additional button that reads "find the modal button and make it black"

ReactDOM.hydrate() and Server-Side Rendering (SSR)

This may sound like a gibberish right now, but the main takeaway is that we can render our React applications on the client or server side. Here’s a quick overview of the main differences between Client-Side Rendering (CSR) and Server-Side Rendering (SSR).

Client-Side Rendering (CSR)

When we create and run our project with create-react-app, it doesn’t show the contents of the page:

Screenshot of the code for the example app
Screenshot of the code for the example app

As you can see from the screenshot, we have only our divs and a reference to our JavaScript bundle, nothing else. So, this is actually a blank page. This means when we first load our page, the server makes a request to the HTML, CSS, and JavaScript. After the initial render, the server checks our bundled JavaScript (or React code, in our case) and paints the UI. This approach has some pros and cons.

  • Quick
  • Static deployment
  • Supports Single Page Applications (SPA)
  • Renders a blank page at initial load
  • Bundle size may be large
  • Not good for SEO

Server- Side Rendering (SSR)

With Server-Side Rendering, we don’t render an empty page anymore. With this approach, the server creates static HTML files which the browser renders.

The site becomes responsive by replacing the static content with the dynamic content. ReactDOM.hydrate() function is actually called on the load event of these scripts and hooks the functionality with the rendered markup.

If you are curious, when we create our project with SSR, we can see the HTML and JavaScript code rendered at initial load:

Screenshot of code for the example app with SSR
Screenshot of code for the example app with SSR

  • Better performance
  • Great for SEO to help us create easily indexable and crawlable websites
  • Fast interactivity
  • Speeds uploading time by running React on the server before serving the request to the user.
  • Creates lots server requests
  • If you have lots of interactive elements on your site, it can slow down the rendering

Demo of ReactDOM.hydrate()

As you can imagine, for this to work, we need to create a server. We will be creating the server with Express, but first we need to do some cleanup.

To run a Node.js server using hydrate, we need to remove all references to the window and document, because we will render our markup in the server, not in the browser.

Let’s go to the Modal.js file and move the document.querySelector inside the modal component:

// Modal.js

import { createPortal } from 'react-dom';
let modalRoot;

const Modal = ({ children }) => {
  modalRoot = modalRoot ? modalRoot : document.querySelector('#modal');
  return createPortal(children, modalRoot);
};

export default Modal;

Next up, we need to change ReactDOM.render to ReactDOM.hydrate inside the src/index.js file:

ReactDOM.hydrate(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

Now, we can create our server. Create a new folder called server and create a file inside this folder named server.js. Install Express with npm install express:

// server/server.js

import express from 'express';
import fs from 'fs';
import path from 'path';
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from '../src/App';

const app = express();

app.use('^/$', (req, res, next) => {
  fs.readFile(path.resolve('./build/index.html'), 'utf-8', (err, data) => {
    if (err) {
      console.error(err);
      return res.status(500).send('Error');
    }
    return res.send(
      data.replace(
        '<div id="root"></div>',
        `<div id="root">${renderToString(<App />)}</div>`
      )
    );
  });
});

app.use(express.static(path.resolve(__dirname, '..', 'build')));

app.listen(3000, () => {
  console.log('Listening on port 3000');
});

Here we have required Express, the fs (file system) module, the path, React, ReactDOMServer.renderToString, and App from our src folder.

The ReactDOMServer.renderToString returns the static HTML version of our app.

// server/index.js

require('ignore-styles');
require('@babel/register')({
  ignore: [/(node_modules)/],
  presets: ['@babel/preset-env', '@babel/preset-react'],
});
require('./server');

Add a script for SSR in package.json: "ssr": "node server/index.js". Run the server with npm run ssr.

Remember, if you make changes in your app, first run npm run build, and then npm run ssr.

Changes to hydrate with React 18

In React 18, a new Suspense-based SSR architecture is introduced. The hydrate method will also be replaced by hydrateRoot.

Conclusion

We have covered a lot about ReactDOM. To summarize, here are the key takeaways we learned throughout this post:

  • React uses a Virtual DOM, which helps us prevent unnecessary DOM repaints, and updates only what has changed in the UI
  • We use the render method to render our UI components to the browser, the most-often used ReactDOM method
  • We use the createRoot method instead of the render method with React 18
  • We can create modals and tooltips with the createPortal method
  • We can unmount a component with the unmountComponentAtNode method
  • We can get access to any DOM node with the findDOMNode method, but the best way is to use ref instead
  • We can use SSR in React with the help of the hydrate method, just be prepared for React 18 and Suspense-based SSR architecture
  • SSR helps us pre-render everything on the server side for better SEO optimization

Get set up with LogRocket’s modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.
  3.      


     to your HTML


  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • ngrx middleware
    • Vuex plugin

Get started now

Target container is not a DOM element

  1. Passing an incorrect id to the document.getElementById() method.
  2. Placing the react script file above the code that creates the div element.
  3. Incorrectly configuring Webpack’s index.html file.

createroot target container not dom element

# Adding multiple classes to the body element in React

You can pass as many classes to the classList.add() method as necessary.


  
  
  
  

If you need to remove one or more classes from the body tag, use the
classList.remove() method.


  
  

# Set styles on the Body element when an event is triggered

You can also set classes to the body element when an event is triggered, e.g. a
button is clicked.

    
       
      
      
  

   
    
      
    
  

We set the onClick prop on the button element, so every time it is clicked,
the handleClick function gets invoked.

# Add a class to the Body element in React

To add a class to the body element in React:

  1. Access the body element as document.body in useEffect or an event
    handler.
  2. Use the classList.add() method to add a class to the body tag.
  3. For example, document.body.classList.add('my-class').
   

 

    
    
    // 👇 add class to body element
    

    // 👇️ adding multiple classes to the body element
    
      
      
      
      
    

    // 👇️ removing classes from body element
    

    // 👇️ checking if the body element contains a class
      
      'body tag contains class'
    
   

   
    
      
    
  

And here is the CSS for the example.

 
   
   

react add class to body

We used the
classList.add()
method to add a class to the body element.


We can access the body element on the document object.

If the class is already present on the body element, it won’t get added twice.

We passed an empty dependencies array to the
useEffect hook because we only
want to add the class to the body element once — when the component mounts.

If you need to remove the classes
when the component unmounts, return a cleanup
function from the useEffect hook.

  
  // 👇 add class to body element
  


     
    // 👇️ removing classes from body element
    // when the component unmounts
    
  
 

# Toggle styles on the Body element when an event is triggered

If you need to toggle styles on the body element when an event occurs,
conditionally check if the styles are already present and remove them if they
are, otherwise, set the styles.

    
       
      
        
        
      
        
        
    
  

   
    
      
    
  

react toggle style on body

If the backgroundColor style is already set on the body element, we remove it
by setting it to null, otherwise, we set it to salmon.

# Pass the correct ID to document. getElementById()

Here is an example of how the error occurs.

   
   

   

// 👇️ passed wrong ID to getElementById() method
 rootElement  
 root  rootElement

root
  
    
  

We passed the wrong ID to the getElementById method, so we ended up passing a
null value to the createRoot() method which caused the error.

To solve the error, make sure that the ID you are using to select the element is
the same ID that you have specified in your index.html file and place the
react script below the code that declares the div element.



  
    <!-- 👇️ must match with index.js  -->
    
  

The ID in the index.html file in the example is root, so that’s what we have
to use when selecting the root element in index.js.

   
   

   

// ✅ correct ID passed
 rootElement  
 root  rootElement

root
  
    
  

# Toggle a class on the body element in React

If you need to toggle a class on the body element when an event occurs, use the
classList.toggle method.

 

    
       
    // 👇️ toggle class on the body element
    
  

   
    
      
    
  

react toggle class on body element

The
classList.toggle
method removes an existing class from the element if the class is present.
Otherwise, it adds the class to the element.

# Set styles on the Body element in React

To set styles on the body element in React:

  1. Access the body element as document.body in useEffect or an event
    handler.
  2. Use the style property to set styles on the body element.
  3. For example, document.body.style.backgroundColor = 'lime'.
   

 

    
    
    // 👇 add class to body element
    

    // 👇️ set style on body element
      

       
      // 👇️ optionally remove styles when component unmounts
        

      
    
   

   
    
      
    
  

And here is the CSS file for the example.

 
   
   

We can access the body element on the document object.

You can use the
style
object to read an element’s styles or set new styles on the element.

  

If you need to add a class to the body element, use the
classList.add
method.


We passed an empty dependencies array to the
useEffect hook because we only
want to set styles on the body element once — when the component mounts.

If you need to remove the styles when the component unmounts, return a cleanup
function from the useEffect hook.

  
    

     
    // 👇️ optionally remove styles when component unmounts
      
  
 

To remove a style, you can just set it to null.

The HTML Code

The HTML code in this tutorial uses JSX which allows you to write HTML tags
inside the JavaScript code:

Example

Create a variable that contains HTML code and display it in the «root» node:

const myelement = (
  <table>
    <tr>
      <th>Name</th>
    </tr>
    <tr>
      <td>John</td>
    </tr>
    <tr>
      <td>Elsa</td>
    </tr>
  </table>
);

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(myelement);

Run
Example »


# Configuring html-webpack-plugin correctly

Another common cause of the error is using Webpack with the
html-webpack-plugin configured incorrectly.

You can use the template option to provide a custom HTML file when using
Webpack. The
html-webpack-plugin will
automatically inject all necessary CSS, JS, manifest and favicon files into the
markup.

First, update the plugins section of your Webpack config file providing a path
to your custom html file.

plugins 
     
      title "Your custom title"
      template 
    



  
    
    <%= htmlWebpackPlugin.options.title %>
  
  
    <!-- 👇️ must match the id in your index.js file  -->
    
  

Now your index.js file should select the element with an ID of root.

   
   

   

 rootElement  
 root  rootElement

root
  
    
  

The createRoot() method takes the root element as a parameter and creates a React root.

The root has a render() method that can be used to render a React element into
the DOM. The root in React is a pointer to the top-level data structure that
React uses to track a tree to render.

Make sure to import createRoot from react-dom/client.
Importing it from react-dom is not supported.

The Root Node

The root node is the HTML element where you want to display the result.

It is like a container for content managed by React.

It does NOT have to be a <div> element and it does
NOT have to
have the id='root':

Example

The root node can be called whatever you like:

<body>

  <header id="sandy"></header>

</body>

Display the result in the <header id="sandy"> element:

const container = document.getElementById('sandy');
const root = ReactDOM.createRoot(container);
root.render(<p>Hallo</p>);

Run
Example »


The createRoot Function

The createRoot() function takes one
argument, an HTML element.

The purpose of the function is to define the HTML element where a React component should be displayed.

# Place your React script tag at the bottom of the body tag

Another common cause of the error is placing the React.js script file above the
code that creates the div element in the index.html file.



  
    <!-- ⛔️ BAD: script runs before div is created  -->
    

    
  

The bundle.js script runs before the div element is created which causes the
error.

Instead, place your script tag below the code that declares the div element.



  
    

    <!-- ✅ GOOD: div already exists  -->
    
  

# Adding a class to the body element when an event occurs

You can also add a class to the body element when an event occurs, e.g.
a button is clicked.

 

    
       
    // 👇️ add class to the body element
    

    // 👇️ toggle class on the body element
    
  

   
    
      
    
  

We set the onClick prop on the button element, so every time it is clicked,
the handleClick function gets invoked.

# Table of Contents

If you got the error when using React testing library, click on the second subheading.

# Remove the strict mode wrapper

If your app doesn’t work after updating to React 18, check whether it’s wrapped
in <StrictMode>.
Strict mode was changed
in React 18 and has more checks than it did in previous versions.

If removing Strict mode fixes your app, remove it during the upgrade and add it back (either at the top or for specific parts of your application) after you have fixed the issues it is pointing out.

  --save-dev @testing-library/react@latest

  --save-dev @testing-library/jest-dom@latest

  --save-dev @testing-library/user-event@latest

Now you should be able to start your tests without getting the error.

   
   

'renders react component'   
  
   divElement  screen
  divElement

In version 13, React testing library drops support for React 17 and earlier and
uses the new
createRoot API by
default.

The ReactDOM.hydrate() method
has also been deprecated in React.js 18 and has been replaced with
hydrateRoot.

   

   

 rootElement  

// Create *and* render a root with hydration.
 root  rootElement 
// Unlike with createRoot, you don't need a separate root.render() call here

The
unmountComponentAtNode()
method has been replaced with root.unmount() in React 18.


container


root

When the unmount() method is called on the root element, it is removed from
the DOM.

The findDOMNode() method
has also been deprecated in StrictMode starting React v 18.0.0.

You can also check out the official
How to Upgrade to React 18 Guide.

Note that there are some surprises around using strict mode. For example, you
might notice that
console.log() gets printed twice.

# React Testing library — ReactDOM. render no longer supported in React 18

Version 13.0.0 of the package adds support for the new createRoot API.

react testing library reactdom render not supported

  --save-dev @testing-library/react@latest

  --save-dev @testing-library/jest-dom@latest

  --save-dev @testing-library/user-event@latest

Make sure to update the versions of all react testing library packages you are
using.

Your index.js file should use the new createRoot API to render your
application.

   
   

   

// 👇️ IMPORTANT: div ID has to match with index.html
 rootElement  
 root  rootElement

// 👇️ if you use TypeScript, add non-null (!) assertion operator
// const root = createRoot(rootElement!);

root
  
    
  

Now you should be able to start your tests without getting the error.

   
   

'renders react component'   
  
   divElement  screen
  divElement

In version 13, React testing library drops support for React 17 and earlier and
uses the new
createRoot API by
default.

The render() method of the
react-dom package is considered legacy starting react-dom version 18.

The method is replaced with the
createRoot() method
that is exported from react-dom/client.

The createRoot() method takes the root element as a parameter and creates a React root.

The root has a render() method that can be used to render a React element into
the DOM. The root in React is a pointer to the top-level data structure that
React uses to track a tree to render.

In the new API, we create a root and then call the render() method on it.

If your app doesn’t work after updating to React 18, check whether it’s wrapped
in <StrictMode>.
Strict mode was changed
in React 18 and has more checks than it did in previous versions.

If removing Strict mode fixes your app, remove it during the upgrade and add it back (either at the top or for specific parts of your application) after you have fixed the issues it is pointing out.

You can also check out the official
How to Upgrade to React 18 Guide.

# ReactDOM. render is no longer supported in React 18

The error «ReactDOM.render is no longer supported in React 18. Use createRoot
instead» occurs because the ReactDOM.render method has been deprecated.

To solve the error, create a root element and use the ReactDOMClient.render
method instead.

reactdom render no longer supported in react 18

Here is an example of how the error occurs in the index.js file of a React v18
app.

   
   
   

// ⛔️ ReactDOM.render is no longer supported in React 18.
// Use createRoot instead. Until you switch to the new API,
// your app will behave as if it's running React 17.
 // 👈️ deprecated starting React 18
  
    
  
  

To solve the error, create a root element and use the ReactDOMClient.render
method instead.

   
   

   

// 👇️ IMPORTANT: use correct ID of your root element
// this is the ID of the div in your index.html file
 rootElement  
 root  rootElement

// 👇️ if you use TypeScript, add non-null (!) assertion operator
// const root = createRoot(rootElement!);

root
  
    
  

The render() method of the
react-dom package is considered legacy starting react-dom version 18.

The method is replaced with the
createRoot() method
that is exported from react-dom/client.

The createRoot() method takes the root element as a parameter and creates a React root.

The root has a render() method that can be used to render a React element into
the DOM. The root in React is a pointer to the top-level data structure that
React uses to track a tree to render.

In the new API, we create a root and then call the render() method on it.

The render Method

The render() method is then called to define the React component that should be rendered.

But render where?

There is another folder in the root directory of your React project, named «public».
In this folder, there is an index.html file.

You’ll notice a single <div>
in the body of this file. This is where our React application will be rendered.

Example

Display a paragraph inside an element with the id of «root»:

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<p>Hello</p>);

The result is displayed in the <div id="root"> element:

<body>
  <div id="root"></div>
</body>

Run
Example »

Note that the element id does not have to be called «root», but this is the standard convention.



Оцените статью
Master Hi-technology
Добавить комментарий