Criando mapas utilizando Leaflet via CDN em um projeto React
Tags: webpack, react, javascript
Published: 2021-09-15 16:09:15

<!-- wp:paragraph {"dropCap":true} -->
<p class="has-drop-cap">Estava trabalhando em um projeto onde teria que ser exibido um mapa com pontos agrupados (marker clusters). O Leaflet é com certeza uma ótima solução para isso. Existem duas opções para adicionar o Leaflet, por npm (<a rel="noreferrer noopener" href="https://react-leaflet.js.org/" target="_blank">react-leaft</a>) ou via <a rel="noreferrer noopener" href="https://pt.wikipedia.org/wiki/Rede_de_fornecimento_de_conte%C3%BAdo" target="_blank">CDN</a>. A segunda opção é mais vantajosa já que o mapa só seria apresentado em uma página e adicionar-lo em npm faz o projeto aumentar de tamanho e o build ficar mais lento.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"className":"is-style-default"} -->
<figure class="wp-block-image is-style-default"><img src="https://github.com/Leaflet/Leaflet.markercluster/raw/master/example/map.png" alt="cluster map example"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph {"fontSize":"medium"} -->
<p class="has-medium-font-size"><br><strong>Como importar o leaflet?</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Segundo a <a rel="noreferrer noopener" href="https://leafletjs.com/examples/quick-start/" target="_blank">documentação</a>, você tem que importar o CSS e o JavaScript, exatamente nessa ordem:</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p></p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":114,"style":{"color":[]},"className":"is-style-default","ampLightbox":true} -->
<figure class="wp-block-image is-style-default"><img src="https://inventordigital.files.wordpress.com/2021/08/image-4.png?w=1024" alt="Imagem mostrando um treco da documentação." class="wp-image-114"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Na hora que for carregar o componente, é possível fazer a mesma coisa "injetando"      o <code>&lt;link&gt;</code> e <code>&lt;script&gt;</code> utilizando:</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p></p>
<!-- /wp:paragraph -->

<!-- wp:code {"style":{"typography":{"fontSize":"12px"}}} -->
<pre class="wp-block-code" style="font-size:12px"><code>// função para adicionar a tag link
const insertLinkCSS = (cssUrl, key = '', id) =&gt; {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = cssUrl
      if (id.length &gt; 0) {
        link.id = id
      }
      if (key.length &gt; 0) {
        link.integrity = key
      }
      link.crossOrigin = ''
      document.body.appendChild(link)
}

//função para adicionar a tag script
const insertScriptSrc = (scriptUrl, key = '', id) =&gt; {
      const script = document.createElement('script')
      script.type = 'text/javascript'
      script.src = scriptUrl
      if (id.length &gt; 0) {
        script.id = id
      }
      if (key.length &gt; 0) {
        script.integrity = key
      }
      script.async = false
      script.crossOrigin = ''
      document.body.appendChild(script)
}</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>Em seguida são chamadas as funções passando o import no inicio do componente:<br></p>
<!-- /wp:paragraph -->

<!-- wp:code {"style":{"typography":{"fontSize":"12px"}}} -->
<pre class="wp-block-code" style="font-size:12px"><code>useEffect(() =&gt; {
   insertLinkCSS(
      'https://unpkg.com/leaflet@1.7.1/dist/leaflet.css',
      'sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==',
    )
    insertScriptSrc(
      'https://unpkg.com/leaflet@1.7.1/dist/leaflet.js',
      'sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==',
    )
  })

  return (
    &lt;div className="App"&gt;
      &lt;header className="App-header"&gt;
        &lt;img src={logo} className="App-logo" alt="logo" /&gt;
        &lt;div id='mapid'&gt; &lt;/div&gt;
      &lt;/header&gt;
    &lt;/div&gt;
  );</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>Dessa forma as tags de import aparecem no html da página.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":76,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large"><img src="https://inventordigital.files.wordpress.com/2021/08/image-1.png?w=1024" alt="" class="wp-image-76"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Repare que estou adicionando um id para cada elemento e eu já vou mostrar o motivo disso.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"fontSize":"medium"} -->
<p class="has-medium-font-size"><strong>Esperando os imports serem carregados</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Antes começar a utilizar, é necessário garantir que os elementos foram carregados antes de começar a configurar o mapa. Uma forma de resolver isso é criar uma forma de verificar quando o <code>&lt;link&gt;</code>  e o <code>&lt;script&gt;</code> aparecem no <code>document</code>:</p>
<!-- /wp:paragraph -->

<!-- wp:code {"style":{"typography":{"fontSize":"12px"}}} -->
<pre class="wp-block-code" style="font-size:12px"><code>//Define todos os ids que precisam ser verificados
const elementsIdConst = {
    LEAFTLET_CSS: 'leafletcss',
    LEAFTLET_JS: 'leafletjs',
    MAP_ID: 'mapid',
  }

function waitLoadElementsById(elementsId, callBack) {
    const formatedArray = elementsId.map(item =&gt; `#${item}`)

    //cria um setInterval para verificar a cada 100 milissegundos
    var elementsExists = setInterval(function () {
      if (document.querySelectorAll(formatedArray).length === elementsId.length) {
        try {
          callBack()
        } catch (e) {
          throw e
        }

        //Se achou todos os elementos, para o setInterval e o setTimeout
        clearInterval(elementsExists)
        clearTimeout(timeout)
      }
    }, 100)

    //timeout para para de rodar o setInterval depois de um tempo
    const timeout = setTimeout(() =&gt; {
      clearInterval(elementsExists)
    },5000)
  }


useEffect(() =&gt; {

   //chama o import do CSS com o id
   insertLinkCSS(
      'https://unpkg.com/leaflet@1.7.1/dist/leaflet.css',
      '',
      elementsIdConst.LEAFTLET_CSS,
    )

   //chama o import do script com o id
    insertScriptSrc(
      'https://unpkg.com/leaflet@1.7.1/dist/leaflet.js',
      '',
      elementsIdConst.LEAFTLET_JS,
    )

    //Com os elementos carregados, chama a função para iniciar o mapa
    waitLoadElementsById(Object.values(elementsIdConst), () =&gt; renderMap())
  })</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"fontSize":"medium"} -->
<p class="has-medium-font-size"><strong>Código completo</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Para ter mais detalhes sobre a implementação você pode ver o código com <a href="https://github.com/NitramTasu/react-map">repositório</a>.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"fontSize":"medium"} -->
<p class="has-medium-font-size"><br><strong>Conclusão</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Utilizando um lib JavaScript via CDN pode ser um pouco mais "complicado" mas pode trazer muitas vantagens para um projeto que pretende escalar, pois você tem muito mais controle dos recursos utilizados e garante que eles só serão carregados na sua aplicação nos momentos certos. Se a sua aplicação é mais simples, então vale a pena adicionar via npm uma lib como "<a rel="noreferrer noopener" href="https://react-leaflet.js.org/" target="_blank">react-leaft</a>" que utiliza leaftlet como componentes react. </p>
<!-- /wp:paragraph -->