Сан Ниан, давайте поговорим о порталах React

внешний интерфейс React.js CSS товар

В v16 React появилась новая фичаPortals. Когда я впервые увидел функцию «Порталы», я не увидел в ней ничего особенного. Однако, когда я недавно имел дело с бизнес-требованием, я понял, чтоPortalsЭто действительно интересно.

воспроизведение сцены

Сначала восстановим требования к продукту.

анализ спроса

В этом функциональном модуле компонент A управляет отображением вкладок первого уровня, компонент B управляет отображением вкладок второго уровня, а компонент C отвечает за отображение текущего активного содержимого вкладок; нажмите на элемент в список заголовков в компоненте C (например, левый, как показано на рисунке), то есть разверните список деталей D правого рисунка в этом модуле. А детали D будут отображаться во всю ширину и высоту в этом модуле.

Реализовать идеи

Приблизительная структура компонента C моделируется с помощью следующего кода.


class C extends React.Component {
    constructor(props) {
        super(props)
        this.state = { visible: false }
    }
    handleClick = () => {
        this.setState({ visible: false })
    }

    render() {
        return (
            <div>
                <Others onClick={this.handleClick} />
                {this.state.visible && <D />}
            </div>
        )
    }
}

Нажав на заголовок в Othors (код моделирования, не беспокойтесь о полной реализации), измените видимое логическое значение в состоянии в компоненте C, а затем определите отображение и скрытие компонента D.

Поскольку требование состоит в том, чтобы отображать 100% полной ширины и высоты, я очень рад написать стиль css компонента D следующим образом:

.d {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    background: #fff;
    z-index: 10;
}

cmd+sПосле сохранения вернитесь в браузер, чтобы увидеть результат, и обнаружите, что требования действительно выполнены, а взаимодействие также соответствует черновому варианту дизайна.

Похоже, то, что я сказал ранее, не имеет никакого смысла. Но когда я допил бутылку со льдом Fat Boy Happy Water, я вдруг понял, что в таком коде обязательно будут баги.

сайт с ошибками

В приведенном выше коде css первая строкаposition: absoluteВот в чем опасность.

мы все знаем приложениеposition: absoluteэлементы смещаются относительно ближайшего нестатически расположенного элемента-предка. В этом примере ближайшим предком компонента D является C. Хотя в настоящее время в компонентах C мы не используем такие вещи, какposition: relativecss, как это, но когда-нибудь, если нам нужно будет использовать его в компонентах Cposition: relativeПри выполнении позиционирования элемента ширина и высота компонента D могут поддерживать только компонент C. (Как показано ниже)

В качестве переднего конца можно сказать, что 100% уменьшается UI, чтобы быть достойным, как передняя часть. Мы не можем сказать товару: «Вы больше не можете добавлять элементы в компонент C в будущем, в противном случае он повлияет на исходную функцию».

Итак, давайте теперь абстрагируемся.В этом требовании мы надеемся, что при нажатии определенного заголовка в компоненте C,Перенесите компонент D в компонент A и повторно используйте его.position: absoluteЗаставьте компонент D заполнить компонент A.

Похоже, нам нужен портал, и когда компонент D выходит через эту дверь, он достигает компонента A.

Портал React World -- Порталы

Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.

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

На самом деле, до того, как я углубился в эту функцию, я не знал, каким словом перевести порталы в уме, и теперь я действительно чувствую, что перевод «портал» действительно является сутью.

Что такое порталы

Давайте кратко рассмотрим порталы:

ReactDOM.createPortal(child, container)

Первый параметр — это визуализируемый дочерний элемент React, а второй параметр — элемент DOM.

модификация кода

Итак, теперь давайте воспользуемся порталами для преобразования нашего кода.

Во-первых, нам нужно получить элемент DOM компонента A, добавив идентификатор к компоненту A, а затем в соответствии сdocument.getElementById('component-a')Получить ссылку DOM A:

<div id="component-a">
    {/* ... 组件A的代码*/}
</div>

CSS компонента A должен добавить абзацposition: relative, чтобы следующий компонент D был абсолютно расположен относительно компонента A.

Затем создайте компонент, который применяет порталы:

import * as React from 'react'
import { createPortal } from 'react-dom'

import './index.scss'
import { ComponentExt } from '@utils/reactExt'

export interface PortalsContainerProps {}

class PortalsContainer extends ComponentExt<PortalsContainerProps> {
    el: HTMLDivElement = null
    constructor(props: PortalsContainerProps) {
        super(props)
        const containers = document.getElementById('component-a')
        this.el = document.createElement('div')
        containers.appendChild(this.el)
    }
    componentWillUnmount() {
        document.getElementById('component-a').removeChild(this.el)
    }
    render() {
        return createPortal(<div className="portals-container">{this.props.children}</div>, this.el)
    }
}
export default PortalsContainer

css часть

.portals-container {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    background: #fff;
    z-index: 10;
}

Наконец, компонент D может быть передан как Children of PortalsContainer:

import PortalsContainer from './PortalsContainer'

...

<PortalsContainer>
    {/* ... 组件D的代码*/}    
</PortalsContainer>

Теперь мы можем взглянуть на окончательную структуру кода в DOM после оптимизации через портал:

И тогда мы найдем что-то очень волшебное. Компонент D, очевидно, является дочерним элементом компонента C, но теперь его структура DOM напрямую вставляется в компонент A через порталы. Это похоже на то, что React Portals открыл для нас портал, позволяющий нашему компоненту D проходить непосредственно в структуру DOM компонента A.

Таким образом, наш компонент D позиционируется непосредственно относительно всего компонента модуля A независимо от того, будет ли компонент C добавлен с элементами позиционирования в будущем.

расходящийся

Когда я узнал о функции портала Portals, я обнаружил, что общие компоненты пользовательского интерфейса, такие как модальные всплывающие окна (Modal), глобальные подсказки (Message) и текстовые подсказки (Tootip), могут применять эту функцию. Вдвойне круто!

Например, вant-designВ компоненте пузырьковой карты Popover он применяется к порталам.

Мы можем посмотреть на составную структуру компонента Popover в React:

Стрелка показывает применение порталов в триггере. А компонент контента посередине — это то место, где контент нашей карты действительно существует.

PS: Если вы заинтересованы в таком компоненте рамы пули, настоятельно рекомендуется взглянутьrc-triggerисходный код.

Эпилог

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

Оглядываясь назад на процесс, начиная с того момента, когда я впервые увидел порталы, и заканчивая углубленной практикой позже, я чувствую, что мне часто нужно больше изучать граничные условия бизнес-сценариев. Может быть, вы также можете получить некоторые знания, которые можно использовать самостоятельно. Вы не можете просто написать код со скрытыми уязвимостями только для того, чтобы соответствовать требованиям и успеть к графику.