Портал React.js

Магия рендеринга элементов вне собственного DOM-дерева

Телепортация в React.js

Знаете ли вы, что React предоставляет специальный и очень мощный компонент под названием «Портал», который может монтировать своих дочерних элементов в совершенно другом месте внутри вашего приложения? Если нет, то пора узнать что-то новое!

Анатомия портала React.js

Вот краткий обзор того, как компонент Portal в React.js работал раньше.

  • Где-то в вашем коде вы монтируете элемент, в котором будут размещены дочерние элементы портала.
  • Тогда вы получите ссылку на этот элемент
  • Затем в другом месте вашего приложения вы монтируете компонент Portal и передаете ссылку, полученную из host-element.
  • Все, что теперь смонтировано внутри портала, будет фактически смонтировано на хосте с первого шага.

Чтобы лучше понять, что происходит, я создал следующие простые визуализации, иллюстрирующие функциональность портала.

Image ee9c2dac28e7

Image 70ff8978589d

Image 886ed0a34985

На основе этих изображений на следующих рисунках показан тот же процесс, но на этот раз с упрощенными примерами кода.

Image dd655b015a22

Image ab94e9940e3a

Портал даже мощнее, чем вы думаете вначале. Он даже позволяет отображать несколько дочерних элементов из нескольких порталов в один и тот же целевой элемент. Порядок установки Порталов также определяет порядок элементов в хосте.

Image de39ae9a02a2

Image b12fdf680f24

Поделиться ссылкой на элемент-хост, который будет содержать всех телепортированных потомков, также очень просто. Официальная документация описывает использование document.getElementById, но вы также можете использовать useRef-hook из React.js. В следующем примере кода демонстрируется простая реализация, которая показывает все, что было описано до сих пор.

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

//
// Link to sandbox:
// 👉 https://codesandbox.io/s/react-16-8-0-forked-d8lm1?file=/src/index.js:0-1067
//

// A simple demonstration of the React-Portal
// w/ multiple portals for the same target.
function App() {
  const ref = useRef(null);
  const [isRefReady, setIsRefReady] = useState(false);

  // Run one more render to actually
  // show the content of our portal.
  // This is necessary, as the setter
  // for the 'ref' alone won't trigger
  // a render.
  useEffect(() => {
    setIsRefReady(Boolean(ref));
  }, []);

  return (
    <div>
      <div>
        {/* The 'target' aka host for our portals. */}
        <header ref={ref} style={{display: "flex", gap: 8 }} />
        <hr/>
      </div>
      <h1>
        Debug
      </h1>
      {isRefReady && createPortal(<button>one</button>, ref.current)}
      {isRefReady && createPortal(<button>two</button>, ref.current)}
      <div>
        {isRefReady && createPortal(<button>three</button>, ref.current)}
      </div>
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector("#root"));

Как компонент Portal позволяет упростить кодирование

Вероятно, наиболее распространенным вариантом использования портала React.js является реализация модального компонента. Вы можете просто определить цель в корне дерева вашего приложения и использовать компонент Portal для монтирования модального окна отовсюду, надежно плавающего над всем содержимым.

Другой вариант использования, который я реализовал, - это использование контейнера вкладок. Поскольку для контейнера вкладок требуется как сам элемент вкладки, так и контент для визуализации при выборе вкладки, я просто определяю два хоста: один для строки вкладок, а другой - для выбранного содержимого.

Затем я просто монтирую массив компонентов, каждый из которых использует портал для элемента вкладки, а также для выбранного содержимого. Это позволяет мне разместить бизнес-логику там, где она действительно должна быть - в каждом отдельном компоненте.

Image 2761da74cf8c

Image e7810bb469d2

Конечно, есть еще более интересные варианты использования Portal-компонента. Самым большим преимуществом, которое, как мне кажется, он дает, является тесная связь бизнес-логики в одном компоненте, дочерние элементы которого затем монтируются в разных местах. Этот шаблон обеспечивает большую гибкость и настройку, но может привести к путанице в вашей кодовой базе, как на самом деле связаны представления.

Также ознакомьтесь с официальной документацией, чтобы узнать все подробности о порталах React.js.