Portal provides a perfect solution to the need to use a pop-up layer. Compared to React Native implementations that use more Modal or absolute positioning, Portal is much simpler and friendlier.

scenario

Dialog box, confirmation prompt box, suspension window these components, generally need to do a higher level View than the current View, but the existing scheme is difficult to jump out of the constraints of the parent container. How do I break this constraint?

Portal provides an excellent solution for rendering child nodes to DOM nodes that exist outside the parent component

The child node can be rendered to the DOM outside of the parent component, which removes this constraint.

Portal: indicates the Portal. There is a game on Steam called Portal2, which uses portals to move each other through levels. The meaning of teleport is very similar, but here is the “teleport” component.

Use and effect

The normal Render method is to mount elements within the component to the nearest DOM node. Portal, on the other hand, mounts elements to any valid DOM node through an API.

ReactDOM.createPortal( children,container,key? )

The parameter types are as follows:

Now start using it in code:

  1. In the index.html file in the public directory, set

    . Then the React element is going to be placed in the Modal div, and the DOM element is going to be the Container.

      <div id="root"></div>
      <div id="modal"></div>
    Copy the code
  2. Do simple encapsulation of the API and build Modal components. Because the Portal API reads the children child component, Portal must be used as a container.

    (You can also add a separate div level to children as in the Portal document.)

    const modalDivElement = document.getElementById("modal");
    
    export default function Modal({ children }) {
      return ReactDOM.createPortal(children, modalDivElement);
    }
    Copy the code

    Using the createPortal API, children in the Modal component are rendered into the modalDivElement regardless of where your Modal is used.

  3. Use Modal components. You embed Modal in a div with a red background, and there’s only one DIV string zhangsan in Modal.

    export default function App() {
      return (
        <div style={{ backgroundColor: "#a00", width: `200px`, height: `100px` }}>
          <Modal>
            <div>zhangsan</div>
          </Modal>
        </div>
      );
    }
    Copy the code

    Now look at the results:

    If you didn’t have a Modal level, Zhanghsan would be in the red area. But why is Modal out there? Check the render structure at this point, here’s the secret:

    You can see that the elements in Modal are rendered into this div with id=” Modal “. To review what happened, our code component structure looks like this:

          <div id="root">
            <div style={{ backgroundColor: "#a00", width: `200px`, height: `100px` }}>
              <Modal>
                <div>zhangsan</div>
              </Modal>
            </div>
          </div>
          <div id="modal"></div>
    Copy the code

    But the actual render structure looks like this (see how the Modal child changes) :

          <div id="root">
            <div style={{ backgroundColor: "#a00", width: `200px`, height: `100px`} < /div>
          </div>
          <div id="modal">
            <div>zhangsan</div>
          </div>
    Copy the code

    The actual render structure is not consistent with the code structure because the Portal in Modal puts child element methods in the specified Container. Child elements are rendered across hierarchies as if they were transported, which is an interpretation of Portal.

Make a Modal popup component

The above just demonstrates the effect of Portal sending sub-components. If you want to achieve the effect of pop-up mask, you only need to add a DIV package to the children child element that needs to be transmitted and add the style. If the pop-up layer style is consistent, you can directly put it into the Modal component. You can also implement and customize styles in children.

Example:

Example code:

export default function App() {
  const [isShow, setIsShow] = useState(false);
  function click(e) {
    console.info("e", e); setIsShow(! isShow); }return (
    <div
      style={{
        backgroundColor: "#a00",
        width: `200px`,
        height: `100px`,}}onClick={click}
    >
      {isShow && (
        <Modal>
          <span>zhangsan</span>
        </Modal>
      )}
    </div>
  );
}
Copy the code

Modal components:

const modalDivElement = document.getElementById("modal");

export default function Modal({ children }) {
  const modalContent = (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        position: "absolute",
        top: 0.left: 0.right: 0.bottom: 0.backgroundColor: `rgba(0.0.0.0.5)`,
      }}
    >
      {children}
    </div>
  );
  return ReactDOM.createPortal(modalContent, modalDivElement);
}
Copy the code

If there are many Modal interactions and content switches, you can further encapsulate and dynamically introduce Modal and subcomponent switches through ref imperative calls.

The Portal event bubbles

The event bubble in Portal follows the React structure, not the actual rendered DOM element structure, which means that the root node above can fetch the event bubble in Modal.

The click event in the page above can get the Modal event triggered by clicking on the red area or the span event inside Modal:

In this way, the interaction control in Modal does not deviate from the current page or component and behaves the same as the event without using Portal.