Add your React component to the balloon above the placemark in react-yandex-maps using React.createPortal

Good afternoon. I want to share information about how I managed to draw my React component inside the pop-up window above the label on the Yandex map. Used the framework from this source.

react-yandex-maps + docks to it

There is quite decent documentation, but something is always missing, especially when using Yandex maps for the first time.

What I was looking for is shown in the picture below.

As it turned out, it is impossible to add a React component directly to react-yandex-map, although this seems to be quite reasonable. There is one way to add an already rendered component (this is done through ReactDomServer.renderToString), but then it would just be a static element.

We need a full-fledged component with access to the state and all the accompanying React functionality. EgorKluch’s comment in issues to react-yandex-map on github about what he used ReacDom.Portal, but he never provided a code example. So let’s take our word for it. Portal is often used for modal windows, which are desirable to be embedded almost on the tag. It was already something, and I began to dig there.

To begin with, a self-sufficient Portal element was created / copied, which will later be used for its intended purpose.

import React, {useEffect} from 'react';
import {createPortal} from 'react-dom';


export const Portal =
    ( { children, getHTMLElementId } ) => {
        // находим искомый HTML по id
        const mount = document.getElementById(elementId)
        // создаём свой div
        const el = document.createElement('div')

        useEffect(() => {
            // добавляем свой див к искомому элементу
            if (mount) mount.appendChild(el)
            return () => {
                // удаляем элемент от искомого при завершении компоненты
                if (mount) mount.removeChild(el)
            }
        }, [ el, mount ])
       
        // отменяем отрисовку при отсутствии искомого элемента
        if (!mount) return null
        // собственно, пририсовываем React-элемент в div к искомому HTML
        return createPortal(children, el)
    }

Then, in fact, the implementation itself:

import React from 'react'
import './yandex-map-restyle-ballon.scss' // стили для карты и балуна
import {Map, Placemark} from 'react-yandex-maps'


export const MapSection = () => {

  const [ activePortal, setActivePortal ] = useState(false)

  return (
    <section className={'map-section'}>
			<Map
         state={ {
         	center:{[55.773733, 37.608771]}, // координаты центра карты
         	zoom: 10, // зум он и есть зум
         	// включаем модули, отвечающие за всплывающие окна над геообъектами
         	modules={ [ 'geoObject.addon.balloon', 'geoObject.addon.hint' ] }
        >
            // Рисуем метку
            <Placemark geometry={ [55.847973, 37.692542] }
              options={
                {
                  preset: 'islands#circleIcon', // список темплейтов на сайте яндекса
                  iconColor: 'green', // цвет иконки, можно также задавать в hex
                } }
              properties={
                {
                iconContent: '2', // пару символов помещается
                hintContent: '<b> Я появляюсь при наведении на метку </b>',
                // создаём пустой элемент с заданными размерами
                balloonContent: '<div id="driver-2" class="driver-card"></div>',
              }	}
              onClick={ () => {
              // ставим в очередь промисов, чтобы сработало после отрисовки балуна
              setTimeout(() => { setActivePortal(true)}, 0)
              } } />
      </Map>
{/* здесь мы активируем портал */}
	{
    activePortal && <Portal getHTMLElementId={ 'driver-2' }>
											// Ставим свой компонент
                      <BallonComponent/>
										</Portal>
  }
  </section>
)

Next, it remains only to style the balloon when drawing

.driver-card {
  width: 240px;
  height: 400px;
  // имейте ввиду, что больше 400px будет показано с прокруткой
}

.ymaps-2-1-79-balloon {
  box-shadow: none !important;
}

.ymaps-2-1-79-balloon__content {
  padding: 0 !important;
  background: transparent !important;
  margin: 0 !important;
}

.ymaps-2-1-79-balloon__layout {
  background: transparent !important;
  border: none !important;
}

It remains to fix the version of Yandex maps when wrapping the entire application

<YMaps version={ '2.1.79' }>
	<App></App>
</YMaps>

Here, it seems, is all. If you have questions, ask, I will clarify the information in this article.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *