Mapas com React usando Leaflet
ReactJS
•
11 de Nov de 2020
👋 Introdução
Neste post vamos desenvolver uma página web para demonstrar, na prática, a integração de Mapas em uma aplicação com React usando Leaflet.
Alguns pontos que vamos abordar:
Geolocalização;
Consumo de API;
Input com Autocomplete usando React-Select;
Integração com Mapas;
Estilização do Mapa e Marcadores.
Leaflet é uma biblioteca JavaScript open-source para trabalhar com Mapas em aplicações web e mobile. Pode ser simplesmente integrada a um site usando apenas HTML, CSS e JavaScript.
Podemos também integrar a Leaflet ao React com a biblioteca React Leaflet, que tem suporte ao TypeScript sendo bastante simples de utilizar. Ambas serão utilizadas em nossa aplicação de demonstração.
Somando todas essas tecnologias e conceitos, no final deste post vamos ter desenvolvido o app Entregas. Vai ser assim:
Fluxo da aplicação Entregas - Clique em HD para melhorar a qualidade do Gif.
Conforme podemos observar na animação acima, quando o usuário digita o endereço, sugestões de locais semelhantes começam a aparecer no autocomplete. Quando algum endereço é selecionado, o pin aparece centralizado no mapa, mostrando a localidade escolhida.
Quando o usuário envia o formulário, o pin 📍 é atualizado para um ícone de pacote 📦. Para acessar os dados da entrega basta clicar no ícone — esse é o fluxo completo :)
📝 Pré-requisitos
Sempre queremos entregar a melhor experiência para nossa audiência.
Para uma melhor experiência com a leitura, você precisa entender o básico de:
yarn create react-app blog-react-maps-leaflet --template typescript
Ou com Npx (npm)
npx create-react-app blog-react-maps-leaflet --template typescript
Agora é só acessar o projeto para começar a codar:
cd blog-react-maps-Leaflet && code .
🌍 Instalando e configurando as bibliotecas
Para trabalhar com mapas instalamos as libs:
yarn add leaflet react-leaflet
A lib react-select vai auxiliar com autocomplete no input de endereço, e o uuid vai gerar um ID:
yarn add react-select uuid
Instalando as tipagens das libs:
yarn add -D @types/react-leaflet @types/react-leaflet @types/react-select
📂 Configurando a variável de ambiente
Sempre que formos trabalhar com APIs de mapas vamos precisar de um Token de acesso com o provedor. Mais uma vez estaremos usando o Mapbox.
É bem simples de criar a conta e pegar o access_token.
Crie um arquivo .env e adicione uma variável:
REACT_APP_ACCESS_TOKEN_MAP_BOX=SEU_ACCESS_TOKEN_MAP_BOX_AQUI
No arquivo .gitignore adicione o .env para não ser enviado ao Github.
No projeto criado com create-react-app é obrigatório a variável começar com REACTAPP
⚠️ Essa variável vai ficar protegida apenas no código, porém fica visível nas requisições, um usuário avançado consegue acessá-la no console/network do navegador.
📍 Configurando a API de Geolocalização.
Para simplificar, não vou criar várias pastas para organizar o projeto, ao invés disso vou manter todos os arquivos dentro de src.
Detalhes importantes sobre a estilização:
Se procurar no código alguma referência por leaflet-popup-content-wrapper você não vai encontrar. Utilizei o inspecionador de código do Navegador para saber quais classes o Leaflet usa para fazermos as customizações nelas.
A mesma coisa para a lib do react-select, no arquivo App.tsx que veremos a seguir. Adicionei classNamePrefix="filter", um prefixo que na DOM aparece, por isso tem o filter__control, por exemplo. Você não encontra uma referência a essa classe no código, ela só aparece quando a tela é montada no navegador.
Adicionamos !important para forçar a estilização e sobrepor os valores padrão que vem na biblioteca.
Último aspecto importante é que o formulário fica flutuando na tela enquanto o mapa fica por baixo. Para isso usamos o z-index e position: absolute para o formulário.
O restante da estilização é mais simples.
Por fim, para concluir a aplicação, vamos criar a lógica e adicionar os componentes necessários.
🗺️ Adicionando o Mapa e o Formulário
Adicione os arquivos SVG dentro da pasta src — package.svg | pin.svg
Código do App.tsx - Formulário e Mapa do site Entregas
Comentários por linha de código:
001 - importa a estilização da biblioteca leaflet;
002 a 014 - importa os arquivos e bibliotecas necessárias;
016 - declara a posição inicial que o mapa será renderizado (poderia usar API do navegador para pegar a localização atual do usuário, dentro do hook useEffect);
018 a 030 - customiza os marcadores package.svg e pin.svg para renderizar no mapa;
032 a 039 - define o contrato da interface Delivery com seus atributos;
041 a 044 - define o tipo Position que será explicado mais a frente seu caso de uso;
046 - começamos a criar a função App que irá conter toda a lógica do formulário e da renderização do mapa;
047 a 058 - são definidas as variáveis de estado do componente App usando useState.
deliveries: array que vai ter todos os dados preenchidos no formulário quando o mesmo for salvo;
position: objeto que representa uma coordenada geográfica, será utilizado para mostrar o marcador (pin.svg) quando o usuário digitar sua localização;
name, complement, address: armazenam os dados do formulário;
location: variável auxiliar para centralizar o mapa assim que usuário escolhe um endereço.
060 a 074 - define a função loadOptions que será disparada toda vez que o usuário digita algum endereço no input. Contém a implementação da busca do local (endereço) usando a API, retorna em formato de um array as opções para o autocomplete, o array é enviado pela função de retorno callback(places) que preencherá as options do select;
076 a 088 - define a função handleChangeSelect executada quando o usuário escolhe uma localização — adicionamos um marcador com setPosition, centralizamos o mapa com setLocation e adicionamos o endereço no estado com setAddress;
090 a 111 - define a função handleSubmit, disparada assim que usuário clica no botão confirmar submetendo o formulário.
Primeiro ela previne o comportamento padrão do navegador de fazer refresh na tela;
Verifica se usuário informou o endereço ou nome — se não, o fluxo é interrompido;
Adiciona no array de deliveries o objeto Delivery com suas propriedades. O id é gerado pelo uuidv4 em formato de string;
Por fim limpa os campos name, address, complement e reseta o position para remover o marcador pin.svg da tela.
113 a 199 - renderiza o mapa e o formulário na tela ;
132 a 139 - adiciona o componente AsyncSelect do React Select:
<AsyncSelect
placeholder="Digite seu endereço..."
classNamePrefix="filter"
cacheOptions
loadOptions={loadOptions}
onChange={handleChangeSelect}
value={address}
/>
Como o próprio nome sugere, ele irá se comportar de maneira assíncrona e, enquanto estiver buscando os dados da API para carregar o options do select, vai exibir um loading no componente.
159 a 197 - monta em tela o componente Map
160 - Centraliza na posição inicial passando location
165 - TileLayer é o desenho do mapa — deixei comentado a TileLayer padrão, que não é muito bonito;
169 - Exibe o marcador da posição que o usuário escolheu no input do endereço, ele sai da tela assim que o usuário clica em confirmar do formulário;
176 a 196 - a cada elemento do array exibe um marcador (package.svg) na posição definida no objeto delivery. Quando usuário clica no marcador aparece um PopUp com os dados sobre a entrega;
202 - exporta a função App que será importada no arquivo index.tsx
Fim da aplicação.
Esse é o resultado que temos no final da implementação:
Imagem do Site Entregas - final do post
Se quiser conferir o código fonte do projeto, clique aqui.
👊 Conclusão
Construímos uma aplicação de pequeno porte, porém com um conteúdo rico em informações sobre integração de mapas no React em 2020, usando React Hooks.
A escolha do Leaflet é interessante, porque é bem fácil de utilizar, a integração com React é muito boa e o suporte ao TypeScript funciona bem;
A escolha do react-select para autocomplete foi devido a sua UI bem bonita, fácil de customizar e a DX (experiência de desenvolvimento) ser agradável com uma documentação excelente.
A documentação do react-select recomenda que seja utilizado CSS-in-JS como exemplo o styled-components, mas, para fins didáticos e práticos, usei o CSS; React-select é bem performática, pesquisei outras libs para fazer o autocomplete mas algumas tinham dependência com Google Places do Google Maps, por exemplo. Como react-select é agnóstica, serviu bem para esse propósito.
Segue aqui alguns desafios para praticar um pouco mais:
🔥 Desafios:
Organizar os arquivos em pastas, separando os componentes;
Criar o botão reset do formulário para apagar os dados digitados e o marcador do mapa caso usuário já tenha escolhido um endereço;
Adicionar horário de entrega;
Criar um botão para abrir a rota no Google Maps a partir da localização da entrega;
Criar um botão no PopUp dos detalhes da entrega para remover a entrega e outro para editar;
Melhorar a UI do PopUp;
Replicar esse mesmo projeto usando o NextJS — bastante coisa deverão ser alteradas.
🚀 Fez algum desafio? Show! Agora você está ficando mais expert! 😎
Posta o link do repositório aqui nos comentários.
E aí, o que achou desse post?
Espero que tenha curtido! 💜
O aprendizado é contínuo e sempre haverá um próximo nível! 🚀
Mapas com React usando Leaflet ReactJS • 11 de Nov de 2020
👋 Introdução Neste post vamos desenvolver uma página web para demonstrar, na prática, a integração de Mapas em uma aplicação com React usando Leaflet.
Alguns pontos que vamos abordar:
Geolocalização; Consumo de API; Input com Autocomplete usando React-Select; Integração com Mapas; Estilização do Mapa e Marcadores. Leaflet é uma biblioteca JavaScript open-source para trabalhar com Mapas em aplicações web e mobile. Pode ser simplesmente integrada a um site usando apenas HTML, CSS e JavaScript.
Podemos também integrar a Leaflet ao React com a biblioteca React Leaflet, que tem suporte ao TypeScript sendo bastante simples de utilizar. Ambas serão utilizadas em nossa aplicação de demonstração.
Somando todas essas tecnologias e conceitos, no final deste post vamos ter desenvolvido o app Entregas. Vai ser assim:
Fluxo da aplicação Entregas - Clique em HD para melhorar a qualidade do Gif. Conforme podemos observar na animação acima, quando o usuário digita o endereço, sugestões de locais semelhantes começam a aparecer no autocomplete. Quando algum endereço é selecionado, o pin aparece centralizado no mapa, mostrando a localidade escolhida.
Quando o usuário envia o formulário, o pin 📍 é atualizado para um ícone de pacote 📦. Para acessar os dados da entrega basta clicar no ícone — esse é o fluxo completo :)
📝 Pré-requisitos Sempre queremos entregar a melhor experiência para nossa audiência.
Para uma melhor experiência com a leitura, você precisa entender o básico de:
Como fazer requisições à API; React & TypeScript CSS NodeJS, Yarn ou Npm configurados e Create React App (CRA) 🔰 Iniciando o Projeto - CRA Para criar um projeto React com TypeScript, execute o comando com Yarn:
yarn create react-app blog-react-maps-leaflet --template typescript Ou com Npx (npm)
npx create-react-app blog-react-maps-leaflet --template typescript Agora é só acessar o projeto para começar a codar:
cd blog-react-maps-Leaflet && code . 🌍 Instalando e configurando as bibliotecas Para trabalhar com mapas instalamos as libs:
yarn add leaflet react-leaflet A lib react-select vai auxiliar com autocomplete no input de endereço, e o uuid vai gerar um ID:
yarn add react-select uuid Instalando as tipagens das libs:
yarn add -D @types/react-leaflet @types/react-leaflet @types/react-select 📂 Configurando a variável de ambiente Sempre que formos trabalhar com APIs de mapas vamos precisar de um Token de acesso com o provedor. Mais uma vez estaremos usando o Mapbox.
É bem simples de criar a conta e pegar o access_token.
Crie um arquivo .env e adicione uma variável:
REACT_APP_ACCESS_TOKEN_MAP_BOX=SEU_ACCESS_TOKEN_MAP_BOX_AQUI No arquivo .gitignore adicione o .env para não ser enviado ao Github.
No projeto criado com create-react-app é obrigatório a variável começar com REACTAPP
⚠️ Essa variável vai ficar protegida apenas no código, porém fica visível nas requisições, um usuário avançado consegue acessá-la no console/network do navegador.
📍 Configurando a API de Geolocalização. Para simplificar, não vou criar várias pastas para organizar o projeto, ao invés disso vou manter todos os arquivos dentro de src.
Na pasta src crie um arquivo apiMapBox.ts
O código acima usa a Fetch API do JavaScript para buscar o local que o usuário informar e retorna os dados.
Exemplo:
https://api.mapbox.com/geocoding/v5/mapbox.places/Brasil.json?access_token=MAP_BOX_ACCESS_TOKEN Resultado:
Para acessar os endpoints do Mapbox, clique aqui.
💅 Estilizando a aplicação No arquivo src/index.css, substitua o conteúdo atual pelo código abaixo.
Basicamente reseta as margens, padding, outline, box-sizing e define a fonte Nunito e um tamanho padrão para os elementos HTML:
Detalhes importantes sobre a estilização: Se procurar no código alguma referência por leaflet-popup-content-wrapper você não vai encontrar. Utilizei o inspecionador de código do Navegador para saber quais classes o Leaflet usa para fazermos as customizações nelas.
A mesma coisa para a lib do react-select, no arquivo App.tsx que veremos a seguir. Adicionei classNamePrefix="filter", um prefixo que na DOM aparece, por isso tem o filter__control, por exemplo. Você não encontra uma referência a essa classe no código, ela só aparece quando a tela é montada no navegador.
Adicionamos !important para forçar a estilização e sobrepor os valores padrão que vem na biblioteca.
Último aspecto importante é que o formulário fica flutuando na tela enquanto o mapa fica por baixo. Para isso usamos o z-index e position: absolute para o formulário.
O restante da estilização é mais simples.
Por fim, para concluir a aplicação, vamos criar a lógica e adicionar os componentes necessários.
🗺️ Adicionando o Mapa e o Formulário Adicione os arquivos SVG dentro da pasta src — package.svg | pin.svg
No arquivo src/App.tsx substitua o conteúdo por:
Código do App.tsx - Formulário e Mapa do site Entregas Comentários por linha de código:
001 - importa a estilização da biblioteca leaflet;
002 a 014 - importa os arquivos e bibliotecas necessárias;
016 - declara a posição inicial que o mapa será renderizado (poderia usar API do navegador para pegar a localização atual do usuário, dentro do hook useEffect);
018 a 030 - customiza os marcadores package.svg e pin.svg para renderizar no mapa;
032 a 039 - define o contrato da interface Delivery com seus atributos;
041 a 044 - define o tipo Position que será explicado mais a frente seu caso de uso;
046 - começamos a criar a função App que irá conter toda a lógica do formulário e da renderização do mapa;
047 a 058 - são definidas as variáveis de estado do componente App usando useState.
deliveries: array que vai ter todos os dados preenchidos no formulário quando o mesmo for salvo; position: objeto que representa uma coordenada geográfica, será utilizado para mostrar o marcador (pin.svg) quando o usuário digitar sua localização; name, complement, address: armazenam os dados do formulário; location: variável auxiliar para centralizar o mapa assim que usuário escolhe um endereço. 060 a 074 - define a função loadOptions que será disparada toda vez que o usuário digita algum endereço no input. Contém a implementação da busca do local (endereço) usando a API, retorna em formato de um array as opções para o autocomplete, o array é enviado pela função de retorno callback(places) que preencherá as options do select;
076 a 088 - define a função handleChangeSelect executada quando o usuário escolhe uma localização — adicionamos um marcador com setPosition, centralizamos o mapa com setLocation e adicionamos o endereço no estado com setAddress;
090 a 111 - define a função handleSubmit, disparada assim que usuário clica no botão confirmar submetendo o formulário.
Primeiro ela previne o comportamento padrão do navegador de fazer refresh na tela; Verifica se usuário informou o endereço ou nome — se não, o fluxo é interrompido; Adiciona no array de deliveries o objeto Delivery com suas propriedades. O id é gerado pelo uuidv4 em formato de string; Por fim limpa os campos name, address, complement e reseta o position para remover o marcador pin.svg da tela. 113 a 199 - renderiza o mapa e o formulário na tela ;
132 a 139 - adiciona o componente AsyncSelect do React Select:
Como o próprio nome sugere, ele irá se comportar de maneira assíncrona e, enquanto estiver buscando os dados da API para carregar o options do select, vai exibir um loading no componente.
159 a 197 - monta em tela o componente Map
160 - Centraliza na posição inicial passando location 165 - TileLayer é o desenho do mapa — deixei comentado a TileLayer padrão, que não é muito bonito; 169 - Exibe o marcador da posição que o usuário escolheu no input do endereço, ele sai da tela assim que o usuário clica em confirmar do formulário; 176 a 196 - a cada elemento do array exibe um marcador (package.svg) na posição definida no objeto delivery. Quando usuário clica no marcador aparece um PopUp com os dados sobre a entrega; 202 - exporta a função App que será importada no arquivo index.tsx
Fim da aplicação.
Esse é o resultado que temos no final da implementação:
Imagem do Site Entregas - final do post Se quiser conferir o código fonte do projeto, clique aqui.
👊 Conclusão Construímos uma aplicação de pequeno porte, porém com um conteúdo rico em informações sobre integração de mapas no React em 2020, usando React Hooks.
A escolha do Leaflet é interessante, porque é bem fácil de utilizar, a integração com React é muito boa e o suporte ao TypeScript funciona bem;
A escolha do react-select para autocomplete foi devido a sua UI bem bonita, fácil de customizar e a DX (experiência de desenvolvimento) ser agradável com uma documentação excelente.
A documentação do react-select recomenda que seja utilizado CSS-in-JS como exemplo o styled-components, mas, para fins didáticos e práticos, usei o CSS; React-select é bem performática, pesquisei outras libs para fazer o autocomplete mas algumas tinham dependência com Google Places do Google Maps, por exemplo. Como react-select é agnóstica, serviu bem para esse propósito.
Segue aqui alguns desafios para praticar um pouco mais:
🔥 Desafios: Organizar os arquivos em pastas, separando os componentes; Criar o botão reset do formulário para apagar os dados digitados e o marcador do mapa caso usuário já tenha escolhido um endereço; Adicionar horário de entrega; Criar um botão para abrir a rota no Google Maps a partir da localização da entrega; Criar um botão no PopUp dos detalhes da entrega para remover a entrega e outro para editar; Melhorar a UI do PopUp; Replicar esse mesmo projeto usando o NextJS — bastante coisa deverão ser alteradas. 🚀 Fez algum desafio? Show! Agora você está ficando mais expert! 😎
Posta o link do repositório aqui nos comentários.
E aí, o que achou desse post?
Espero que tenha curtido! 💜
O aprendizado é contínuo e sempre haverá um próximo nível! 🚀