Nasza strona używa cookies. Dowiedz się więcej o celu ich używania i zmianie ustawień w przeglądarce. Korzystając ze strony, wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki. Rozumiem

Portals - sprawdź nową funkcjonalność React 16

Poznaj funkcjonalność znacznie ułatwiającą pracę z elementami typu modal, popover czy tooltip - dzięki niej programowanie z React staje się łatwe i przyjemne.
11 09 2018

Wraz z nadejściem Reacta w wersji 16 pojawiła się funkcjonalność o dość osobliwej nazwie - Portal. Pierwsze, co przyszło mi na myśl, gdy o niej usłyszałem, to film Gwiezdne Wrota i odkopany przez (jakżeby inaczej) amerykańskich naukowców kamienny portal do podróży międzygwiezdnych. Bądź co bądź, nazwa dość dobrze oddaje cel, w jakim funkcjonalność została stworzona. Portal w ReactJS umożliwia nam przeniesienie elementów strony w miejsce bardzo odległe od komponentu - rodzica, będące poza jego hierarchią, a jednocześnie zachowuje się jak jego komponent - dziecko.

Kiedy się nam przyda?

Czasem mamy potrzebę, aby pewien element strony wyświetlić ponad wszystkimi, jak w przypadku modala. W React możemy mieć do czynienia ze skomplikowaną strukturą komponentów, np.:

<App>
    <HomePage>
        <Form>
            <FormGroup>
                <FormInput />
                <InputAlert />
            </FormGroup>
        </Form>
    </HomePage>
</App>


Przy tej zawiłej hierarchii i stylowaniu komponentów - rodziców (które mogą posiadać chociażby atrybut overflow: hidden) wywołanie ostrzegającego nas przed jakąś akcją modala w komponencie <InputAlert /> może okazać się problemem nie do obejścia. Z pomocą przyjdzie nam właśnie funkcjonalność createPortal, która wyświetli element poza strukturę DOM aplikacji.

Implementacja

Zakładając, że główny komponent renderowany jest w kontenerze o id='root', dodajmy w linii poniżej kontener, w którym będziemy chcieli wyświetlić nasz modal:

<body>
    <div id="root"></div>
    <div id="portal"></div>
</body>


Teraz stworzymy komponent o nazwie Portal, w którym zaimplementujemy jego działanie. Będzie pośrednikiem pomiędzy komponentem reactowym, a elementem w drzewie DOM, gdzie wyświetli się modal.

Potrzebujemy zaimportować pakiet ReactDOM, gdzie znajduje się funkcjonalność tworzenia portali.

import ReactDOM from 'react-dom';


Komponent musi
wiedzieć, gdzie ma otworzyć portalpodajemy więc element w DOM, gdzie się otworzy:

const portalRoot = document.getElementById('portal');


Jako, że nie będziemy korzystać ze stanu komponentu, tworzymy functional component:

export const Portal = props => {
const {
    children
} = props; //


skorzystamy z destrukturyzacji ES6 return ReactDOM.createPortal(children, portalRoot); // tworzymy portal i wrzucamy element, który zostanie wyświetlony przez portal };

Przekazujemy w propie children element, jaki ma być wyświetlony przez portal i - korzystając z funkcji createPortal - przekazujemy tenże element w pierwszym parametrze. W drugim podamy element w drzewie DOM, w którym otworzy się portal.

Kolejnym krokiem będzie dodanie komponentu Modal - elementu wyświetlanego przez portal, zdefiniowanego akapit wyżej jako prop children. Będzie on komponentem bezstanowym - także tu również skorzystamy z arrow function:

import React from 'react';
import PropTypes from 'prop-types';
export const Modal = props => {
    const {
        toggleModal,
        visibility
    } = props;
    return (
        visibility && < div className = "modal-component" >
        <
        div className = "modal-component__body" >
        <
        h1 > Modal title < / h1 > < div > It works! < / div > < button className= "btn" > Click and do nuffin' </
        button > < button className = "btn"
        onClick = {
            toggleModal
        } > Close < / button > <
        / div > </
        div > );
};
PropTypes.propTypes = {
    visibility: PropTypes.bool,
    toggleModal: PropTypes.func.isRequired
};


Komponent przyjmuje dwa propy visibility oraz toggleModal, obsługujące wyświetlanie. Wykorzystany został tutaj też pakiet prop-types do kontrolowania typów ww. propów.

Następnie komponent Modal umieszczamy jako children w komponencie <Portal>. W ten sposób komponent Modal zostanie przekazany do komponentu Portal.

<Portal>
    < Modal toggleModal={this.onModalClick} visibility={this.state.showModal}>
        < h1> Modal title </ h1>
        < div> It works ! </ div>
    </ Modal>
    < /Portal>


Dodamy jeszcze przycisk, który będzie sterował wyświetlaniem Modala:

<button className="btn" onClick={ this .onModalClick}>
    Wyświetl modal < /button>


Po kliknięciu możemy zobaczyć, że modal faktycznie wyświetla się poza divem 'root'. Ponadto zachowuje dostęp do zmiennych i funkcji zdefiniowanych w ramach komponentu rodzica.

Co ciekawe, portal - który przecież znajduje się teraz poza głównym kontenerem 'root' - nadal zachowuje się jak jego element. Weźmy chociażby zjawisko event bubblingu. Dodajmy w komponencie Modal przycisk, który nie będzie miał zdefiniowanej żadnej akcji:

<button className= "btn" >Click me and I 'll do nuffin' < /button>


Następnie w komponencie rodzicu złapiemy event, który zgodnie z teorią event bubblingu przeszedł do komponentu - rodzica ze względu na brak zdefiniowanej akcji onClick:

state = {
    clicks: 0,
    showModal: false
};
onModalClick = () => {
    this.setState(prevState => ({
        showModal: !prevState.showModal
    }));
};
handleClicking = () => {
    this.setState(prevState => ({
        clicks: prevState.clicks + 1
    }));
};


Wyświetlając liczbę kliknięć ustawianą w stanie:

<strong> Number of clicks: { this .state.clicks}< /strong>


zauważymy, że - pomimo zupełnie innego położenia - Portal nadal zachowuje się tak, jakby był elementem React Tree.

Podsumowanie

Wersja 16 ReactJS przyniosła nam kilka ciekawych udogodnień, między innymi właśnie funkcjonalność Portal. Nie dość, że znacznie ułatwia ona pracę z elementami typu modal, popover czy tooltip, to zachowuje zalety elementu drzewa reactowego. Dużym plusem jest również jej łatwość implementacji i czytelność. W zasadzie jedną linią kodu otwieramy Portal i wrzucamy do niego zawartość. Elementy takie Portal sprawiają, że programowanie z wykorzystaniem biblioteki React jest łatwe i przyjemne.