نحوه کار با React Portals

در React رفتار پیشفرض به این صورت است که app بر روی یک single DOM node ارائه (render) می شود.بیشتر اوقات این موضوع مشکلی ندارد اما گاهی اوقات شما نیاز پیدا می کنید که یک محتوایی رو خارج از app root و under DOM nodes ضمیمه کنید.React یک روش عالی برای انجام این کار دارد:portals.بنابراین شما می خواهید یک سری children رو در خارج از app root DOM node نمایش دهید؟خوندن رو ادامه بدید تا یاد بگیرید.
مشکل
قطعه کد زیر نمایش دهنده یک فایل minimal برای استفاده از React است:
1 2 3 4 5 |
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render(<App/>, document.getElementById('root')); |
و فایل استاندارد index.html به شکل زیر است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <title>React App</title> </head> <body> <div id="root"></div> </body> </html> |
در بسیاری از اپلیکیشن ها، داشتن یک درخت کامل از element ها در under a single DOM node مشکل ایجاد نخواهد کرد.با این وجود ، دیر یا زود با شرایطی روبرو می شوید که باید یک component را خارج از app’s root خود render کنید.یکی از موارد رایجی که شما به آن برخورد می کنید مواردی است که شما می خواهید یک سری css ها رو در بالای یک node نمایش دهید(z-index, overflow و position).
کامپوننت هایی مثل modals , floating menus, popovers, toasts, chat widgets و tooltips می توانند از داشتن root خودشان بهره ببرند.
React portals
React portals یک راه حل رسمی برای حل مشکل فوق است.این یک راه برای render کردن children در خارج از app’s root است.همانطور که از اسم آن پیداست ، به عنوان مثال ، عناصر “teleports” را به نقطه دیگری ، مانند node خواهر و برادر به node ریشه برنامه ، انتقال می دهد. این بخش استخراج شده از برنامه شما معمولاً تحت کنترل React رفتار خواهد کرد.
یک مثال
برای نشان دادن نحوه کار portals یک chat widget رو پیاده سازی می کنیم.
در ابتدا یک root جدید در فایل index.html به عنوان خواهر و برادر root node ایجاد می کنیم.اینجا جایی خواهد بود که کامپوننت widget ما render خواهد شد:
1 2 3 4 |
<body> <div id="app-root"></div> <div id="chat-widget-root"></div> </body> |
حالا باید یک فایل برای کامپوننت widget خود به نام chat-widget.js ایجاد کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import './chat-widget.css'; const chatWidgetRoot = document.getElementById('chat-widget-root'); export class ChatWidget extends Component { state = { open: false }; handleOpenButtonClick = () => this.setState({ open: true }); handleCloseButtonClick = () => this.setState({ open: false }); render() { return ReactDOM.createPortal(this.renderWidget(), chatWidgetRoot); } renderWidget() { /*...*/ } } |
در کد بالا چه اتفاقی می افتد:
- در ابتدا ماژول ReactDOM را ایمپورت می کنیم که برای render کردن portal استفاده می شود.
- سپس یک query برای گرفتن refrence به root ایی که قرار است render شود ایجاد می کنیم.
- در گام بعدی یک state برای مدیریت باز و بسته شدن chat widget ایجاد می کنیم.
- سپس از یک جفت تابع برای مدیریت کردن state تعریف میکنیم.
- پس از آن ، درrender method کامپوننت ، در نهایت portal را ایجاد می کنیم. این کار با فراخوانی ReactDOM.createPortal و پاس دادن children به عنوان اولین argument و گرهroot DOM هدف به عنوان argument دوم انجام می شود. ساختار widget’s عمداً به روشی جداگانه استخراج شده است ، که در زیر مشاهده خواهیم کرد.
حالا نگاهی به renderWidget می کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
nderWidget() { return ( <> { !this.state.open && ( <button className="chat__open-button" onClick={this.handleOpenButtonClick}>Need help?</button> ) } { this.state.open && ( <div className="chat__window"> <header className="chat__header"> <h3 className="chat__title">Live support</h3> <button className="chat__close-button" onClick={this.handleCloseButtonClick}> <img src="https://d2ffcs5wrcif.cdn.shift8web.com/arrow-down-icon.svg" alt=""/> </button> </header> <div className="chat__messages"/> <textarea className="chat__message-input" placeholder="Type your message here"/> </div> ) } </> ); } |
کد بالا حاوی markup ویجت ما است و هیچ ارتباط مستقیمی با منطق rendering پورتال ندارد. ما همچنین با استفاده از خاصیتtoggling ، تغییر پنجره چت را با state کنترل می کنیم. توجه داشته باشید که برای wrap کردن component’s markup ، از یک جفت tag خالی (<> و </>) استفاده می کنیم. این یکsyntax عالی به نام Fragments است ، یک ویژگی React برای گروه بندی چندین children بدون اضافه کردن یک گره node به DOM. از آنجا که این یک syntaxجدید است ، شاید مجموعه ابزارهای React شما هنوز از آن پشتیبانی نمی کنند. اما نگران نباشید ، می توانید با خیال راحت آن را با Fragment جایگزین کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import React, { Component, Fragment } from 'react'; /*...*/ export class ChatWidget extends Component { /*...*/ renderWidget() { return ( <Fragment> {/*...*/} </Fragment> ); } } |
برای تأیید صحت کار portal ، فقط باید کامپوننت ChatWidget را ایمپورت کنید و آن را در هر کجای برنامه خود render کرد. در مثال خود ، ما آن را در کامپوننت ForgotPassword قرار داده ایم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import React from 'react'; import { ChatWidget } from './chat-widget'; import './forgot-password.css'; export default () => ( <div className="forgot-password"> <h1 className="app-external__title">Recover your account</h1> <form> <input type="text" placeholder="Enter the last password you remember..."/> <button onClick={e => e.preventDefault()}>Continue</button> </form> <ChatWidget/> </div> ); |
و سرانجام کامپوننت ما به خوبی کار می کند.
نتیجه گیری :
- Portals یک راه اختصاصی ارائه شده توسط React برای نمایش children خارج از root node اصلی اپلیکیشن است.
- Children ارائه شده(render شده) توسط portals هنوز تحت کنترل React’s است.
- Portals فقط بر روی ساختار DOM تاثیر می گذارد و تاثیری بر روی React components tree ندارد.
امیدوارم که این مقاله براتون مفید واقع شده باشه.اگر قصد دارید React رو به صورت حرفه ای کار کنید،پیشنهاد میکنم دوره جامع آموزش ReactJs رو مشاهده کنید.
[منبع]
دیدگاهتان را بنویسید