Портал

Портал нь хүүхэд компонентуудтыг ДОМ-руу эцэн компонентын ДОМ доторх байрлалаас гадуулр байршуулах сонгомол аргачилал юм.

ReactDOM.createPortal(child, container)

Эхний аргумент (child) бол ямар нэг рендерлэх боломжтой React хүүхэд, элемент, текст, эсвэл хэлтэрхий гэх мэт. Хоёрдах аргумент (container) нь ДОМ элемент.

Жэрэглээ

Ихэвчлэн, компонент-н рендер мэтодоос элемнент буцаахад, энэ нь ДОМ байдлаар хамгийн ойрын эцэг node-д хүүхэд болон ордог:

render() {
  // React шинэ дэв үүсгээд children-г түүн дотор рендер хийнэ.
  return (
    <div>
      {this.props.children}
    </div>
  );
}

Харин зарим тохиолдолд, ДОМ-ын өөр хаа нэгтээ хүүхэд болгон оруулах хэрэгцээ гардаг:

render() {
  // React шинэ div *үүсгэхгүй*. `domNode` дотор children-г рендер хийидэг.
  // `domNode` нь DOM доторх байрлалаас үл хамаарсан ямар нэг бодит DOM node байна.
  return ReactDOM.createPortal(
    this.props.children,
    domNode
  );
}

Порталын нийтлэг хэрэглээ бол эцэг компонент нь overflow: hidden эсвэл z-index загвар ашигласан хэдий ч үүнээс үл хамаараад хүүхэд компонентоо харуулах шаардлагатай үе юм. Жишээ нь: dialogs, hovercards, and tooltips.

Тэмдэглэл:

Порталтай ажиллаж байхдаа гарын фокус зохицууалт маш чухал болхыг санаарай.

Модал диалогын хувьд бүх хүн WAI-ARIA Модал Зөвшөөрөгдсөн Практик-н дагуу харилцах боломжтой байгааг хянаарай.

CodePen дээр турших

Порталын Эвент Бөмбөлөгүүд

Хэдий портал ДОМ-н модны хаана ч байх боломжтой ч бусад талаараа энгийн React хүүхэдтэй ижил хэвийн ажиллагаатай. Контекст зэрэг боломжууд child нь портал эсэхээс үл хамаараад яг ижилхэн, учир нь портал нь ДОМ мод-ы хаана байгаагаас хамааралгүй React мод-нд оршиж байгаа юм.

Энэ нь эвент бөмбөлөгт мөн ижил. Портал дотроос эвент нь дуудагдaхад, тэдгээр элементүүд нь ДОМ мод дотор дээд үе биш байсан ч, React мод доторх өөрийн дээд үеүүдрүү түүнийг тараадаг. Дараах HTML бүтцийн дагуу:

<html>
  <body>
    <div id="app-root"></div>
    <div id="modal-root"></div>
  </body>
</html>

#app-root доторх эцэг компонент нь ижил түвшний #modal-root node-с ирэх чөлөөтэй, эвент хөөсийг барих боломжтой.

// Эдгээр конэйнерүүд нь ДОМ дотор ижил түвшинд байгаа
const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {
    // Портал элемент нь Модалын хүүхдүүд суурилагдсаны
    // дараа ДОМ модонд ордог, өөрөөр хэлбэл хүүхдүүд нь
    // тусдаа ДОМ node-д суурилагдана. Хэрвээ хүүхэд
    // компонент дөнгөж суурилаад ДОМ модонд залгагдах
    // шаардлатай бол, жишээ нь ДОМ node-г хэмжих,
    // `autoFocus`-г удамдаа ашиглах зэрэг шалтгаанаар,
    // Модал дээр state оруулаад Модал ДОМ модонд орход
    // зөвхөн хүүхдүүдийг рендер хийх хэрэгтэй.
    modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    modalRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el,
    );
  }
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {clicks: 0};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    // Хүүхэд доторх товчыг дарагдахад энэ дуудагдана,
    // ДОМ-н шууд удамд тухайн товч нь байхгүй байсан ч
    // Эцгийн state-г шинэчилнэ.
    this.setState(state => ({
      clicks: state.clicks + 1
    }));
  }

  render() {
    return (
      <div onClick={this.handleClick}>
        <p>Number of clicks: {this.state.clicks}</p>
        <p>
          Open up the browser DevTools
          to observe that the button
          is not a child of the div
          with the onClick handler.
        </p>
        <Modal>
          <Child />
        </Modal>
      </div>
    );
  }
}

function Child() {
  // Энэ товчны дарах эвент нь эцэгрүү дамжих ба учир нь
  // `onClick` аттрибут тодорхойлогдоогүй юм
  return (
    <div className="modal">
      <button>Click</button>
    </div>
  );
}

ReactDOM.render(<Parent />, appRoot);

CodePen дээр турших

Портал-с ирэх эвент-г эцэг компонент дээр барих нь портал-с шууд хамааралгүй илүү уян хатан abstraction хөгжүүлэлт хийх боломж олгодог. Жишээ нь: <Modal /> компонентыг рендер хийхэд эцэг компонент нь түүний эвентүүдийг портал ашиглаж хийгдсэн үгүйгээс хамаарагүй бариж авах болмжтой юм.