Портал React.js

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

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

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

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

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

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

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

Image ee9c2dac28e7
Image ee9c2dac28e7
Image 70ff8978589d
Image 70ff8978589d
Image 886ed0a34985
Image 886ed0a34985

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

Image dd655b015a22
Image dd655b015a22
Image ab94e9940e3a
Image ab94e9940e3a

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

Image de39ae9a02a2
Image de39ae9a02a2
Image b12fdf680f24
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 2761da74cf8c
Image e7810bb469d2
Image e7810bb469d2

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

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

Suggestions

Categories