FrontEndMentor.io Challenge #1
Tags: react, javascript, regex
Published: 2021-09-08 22:54:01

Como parte da minha rotina de estudos, no início dessa semana comecei um novo projeto do [FrontEndMentor.io](https://www.frontendmentor.io/). O desafio escolhido da vez foi o [IP Address Tracker](https://www.frontendmentor.io/challenges/ip-address-tracker-I8-0yYAH0), uma aplicação simples onde ao acessá-la o usuário deve poder ver seu próprio endereço IP público, entre outras especificações que serão listadas abaixo. 

## Briefing 

O IP Address Tracker, como o nome já diz, deve poder rastrear o endereço IP público do usuário e retorná-lo com algumas outras infos, como cidade, estado, timezone e provedor de internet. Um pin deve ser marcado no mapa, utilizando a `[lat,lng]` retornado pelo Ipify. 

O desafio possui um layout pré-definido, com uma versão para desktop otimizada para 1440px, e outra para mobile para 375px. Além disso, foi disponibilizado um style-guide, com a fonte com  as variações a serem usadas e a paleta de cores (em hsl).

Apesar do dev ser livre para usar quaisquer libs, frameworks e ferramentas que julgue necessário ou que simplesmente queira praticar, o desafio sugere que sejam usadas a IP Geolocation API da [Ipify](https://geo.ipify.org/), para pesquisar os endereços IP, e o [LeafletJS](https://leafletjs.com/) para gerar o mapa. 

##Tools

Como estou desenvolvendo minhas habilidades em React, o escolhi. Junto ao React, escolhi trabalhar com o [react-leaflet](https://react-leaflet.js.org/) para exibir o mapa, [styled-components](https://styled-components.com/) para estilização, [Unform](https://unform.dev/) e [Yup](https://www.npmjs.com/package/yup) para o "formulário" e sua validação.

##O projeto

Para esse desafio optei por usar CRA com `--template typescript`.

Sou iniciante e os primeiros contatos que tive com desenvolvimento foram para web, lidando com telas maiores, porém conforme fui me aprofundando nos meus estudos tive contato com o conceito de mobile first, então resolvi aplicá-lo aqui. Assim, quando o CRA terminou de criar o projeto, já fui logo deletando aqueles arquivos que não iria usar, deixando apenas o mínimo necessário para mostrar em tela o tão querido `<h1>HELLO WORLD</h1>`.

Com aquele h1 todo feioso em tela, comecei resetando os estilos, e fazendo as primeiras configurações CSS, já adicionando a fonte e cores indicadas no projeto. 

Assim, iniciei meu primeiro componente que, *na falta de um nome melhor*, chamei de Map 😛. Neste componente, criei uma `<div className='background'>` , composta por uma imagem (disponibilizada pelo FrontEndMentor), e pelo mapa, disposto um logo abaixo do outro com `display:flex` e `flex-direction: column`. Como optei por tratar esse componente como um background também apliquei `z-index: -2` nele, para que quando o chamasse, ele já fosse para baixo dos outros componentes. Ainda precisei usar `position: absolute` para organizá-lo melhor na tela.

Com o mapa já em tela (usei o *example code* disponibilizado no site do React-Leaflet para renderizá-lo), parecia que o pior já havia passado 🤓. Então comecei a criar os próximos componentes, que foram o *searchbar*, que chamei apenas de `<Input />`(na época não me toquei que ele não era apenas um simples input haha), e um `<Card />` onde seria apresentado todas as infos já descritas anteriormente. 

Criei também um componente chamado `<Layout />` onde basicamente organizei todos os outros componentes para chamá-los de uma só vez no `App.tsx`. Achei que ficou legal dessa forma. Então com uso de media query e mudando alguns `flex-direction` adaptei o layout para sua versão desktop e ficou bonitão. 

Até aqui foi de boa, mesmo de forma estática já tinha tudo o que precisava para começar a "dar vida" aquele corpo inerte de `<div>` , `<h1>` e tantas outras tags. 

Para me conectar a API da Ipify usei o [Axios](https://axios-http.com/docs/intro), uma solução um pouco mais bonita e performática do que o `fetch()` do próprio javascript. Precisei também de uma API_KEY que foi bem rápido de ser criada, e já tratei de salvá-la no meu `.env`. 

Após isso, tentei fazer a chamada API dentro do componente `<Layout />` e já passar os dados para os devidos componentes por meio das *props*. Não tão surpreendentemente assim, tudo funcionou bem, só precisei fazer alguns ajustes CSS, e na minha tela já estava aparecendo meu endereço IP, minha localização, timezone e provedor de internet 🎉. 

"Haha! Sou melhor do que imaginei, me aguarde *mercado de trabalho* 😎", pensei enquanto analisava o que havia conseguido fazer em apenas um dia. Bem, com o coração quentinho por tudo ter dado certo no primeiro dia, fui descansar, mas antes resolvi anotar o que eu queria terminar no dia seguinte. 

"Preciso mudar o mapIcon para o correto, fazer funcionar a barra de pesquisa,  e o mapa precisa mostrar a localização da pesquisa do usuário. Simples!" 

No outro dia, ao iniciar a aplicação um erro apareceu na tela, alguma coisa como *"Cannot read property of undefined"* blábláblá🙄.

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH!

Tudo bem, um erro simples, vai ser fácil de corrigir. Identifiquei que a chamada API da Ipify demorava um pouco para responder e salvar o `response.data` no `state` e eu já estava criando os componentes com esses dados. Coloquei algumas condições para que enquanto a chamada não finalizasse exibisse "Loading…" nos componentes e *voilà*, voltou a funcionar. 

S U A V Ã O! Talvez não tenha sido a melhor solução, mas definitivamente estava funcionando. 

Hora de focar no que eu tinha que resolver neste dia. Comecei fazendo a *searchbar* ficar funcional. Com algumas linhas de código já estava recebendo os dados requisitados e apresentando-os no console do Chrome. 

Mas tinha uma coisa que eu não havia me atentado até então: para cada tipo de pesquisa eu usaria um *endpoint* diferente. Para pesquisar por um endereço IP eu deveria usar `&ipAdress=` e para pesquisar por um domínio usaria `&domain=`.
 
Resolvi isso de forma muito classuda. Há uns dias atrás estava fazendo um módulo do curso de javascript da [FreeCodeCamp](https://www.freecodecamp.org/), justamente sobre expressões regulares, as RegEx. Notei que esse desafio seria uma ótima oportunidade para utilizá-las e treinar minhas habilidades, fiquei felizão. 

Desta forma, criei uma variável que verifica se a pesquisa é ou não um endereço IP (na verdade só verifica se a pesquisa começa com um número ou não, se começar assumi que os próximos caracteres também seriam números, logo seria um endereço IP). Assim surgiu a tal forma classuda dita lá em cima, `const isSearchAnIPAddress = !!data.search.match(/^\d/)`.

Usei `isSearchAnIPAddress` em um `if` de forma muito legível. `if(isSearchAnIPAddress)` usa o endpoint de pesquisa por endereço IP, senão, usa o endpoint para domínios. 

Fiz os testes novamente e G E E E E E E Z Z! To voando nesse desafio. 

>And I found a dream worth fighting for
>And nothing can stop me
>Nothing can stop me
>Nothing can stop me
>Nothing can stop me nooooow
>([Nothing can stop me now](https://www.youtube.com/watch?v=2ACksQK4P8Q&ab_channel=MarkHolman-Topic) - Filme ruim da Disney que ninguém assistiu)

Porém, ainda tinham coisas a serem feitas, como, por exemplo, mudar o mapIcon, o que foi até que fácil. Lembra que eu falei que tinha participado de um NLW da Rocketseat, pois é, foi só copiar o código que já estava pronto lá. Aproveitei e acessei a plataforma para assistir o [Diego Fernandes](https://twitter.com/dieegosf) explicando como isso funcionava, porém os vídeos estavam fora do ar (a galera do twitter da Rocketseat me informou que houve um probleminha com o Vimeo, mas alguns dias após entrei novamente e já estavam todos lá).

Também ainda tinha que tratar o valor do input para ser usado na requisição de forma correta. Incialmente pensei em criar uma máscara para o input que seria usada apenas se o primeiro caractere digitado fosse um número. Essa máscara iria adicionar um ponto a cada três números digitados, e seria basicamente isso mesmo (tentaria fazer isso com RegEx). 

Ao pesquisar mais sobre os endereços IP, descobri que eles são formados basicamente por 4 "grupos" de números separados por pontos, e que esses números podem variar de 0 a 255. Isso abre margem para o usuário pesquisar por um IP no formato `x.x.x.x`, `xxx.xxx.xx.xxx` ou vários outros. Assim resolvi abandonar a ideia da máscara, por não saber como aplicar uma que fosse "mutável" (provavelmente há como fazer isso, mas não consegui resolver com essa abordagem). 

Outro problema que identifiquei foi que o domínio pesquisado deveria estar no padrão `www.domain.com`, não podendo ter o http:// ou https://. Para resolver isso, dentro do else daquele if lá de cima 👆, verifiquei se a pesquisa começava com http com `.startsWith('http')`. Caso a verificação retorne true então a pesquisa seria tratada da seguinte forma: 
 `const HTTPRegex = /^https?:\/\//`;
 `const httpString = data.search.match(HTTPRegex)` //caso o match seja true, retorna um objeto onde o primeiro elemento é exatamente o resultado da regex acima, neste caso http:// ou https://;
 `const httpLength = httpString?.[0].lenght` // aqui fica armazenado o número de caracteres da `const httpString`;
 por fim, usei o `httpLength` para "cortar" a string da pesquisa, utilizando `.slice(httpLength)` à string. Desta forma, caso o usuário pesquise, por exemplo, por https://www.facebook.com a string utilizada na requisição a API seria apenas www.facebook.com. 

Com o value do input da pesquisa já sendo tratado, agora era hora de salvar `response.data` em um estado que criei chamado `user` (as vezes eu não penso em um nome de variável bom e vai essas coisas feias mesmo hahaha 🤪). Essa parte foi simples, consegui salvar no estado de boa, e conforme ia realizando novas pesquisas os dados iam sendo salvos e exibidos em tela, porém o mapa continuava estático. Na verdade, o mapIcon mudava de local, mas o centro do mapa não. 

Após revisitar o código do NWL#3 não achei o problema, resolvi ir procurar na documentação do React-Leaflet e descobri que na sua versão 3, o `<MapContainer>` agora serve como um *provider* que habilita o uso de alguns *hooks*, entre eles o `useMap()`, que por sua vez possibilita usar o `.flyTo()`, que serve para *voar* (tem toda uma animação bacana para isso) para uma nova localização. Para usar o `useMap()` , tive que mover o `<TileLayer>` e o `<Marker>` para um novo componente que foi chamado dentro do `<MapContainer>` . Nomeei esse novo componente de `<MapContent>` , e fiz ele receber de seu componente pai uma props chamada `position`, que nada mais é do que um objeto composto pelas propriedades `lat: number` e `lng: number`, já no formato correto para passar como parâmetro para aquela função `flyTo()`. Com isso o mapa também já estava funcionando. 

Até aqui a aplicação já estava ficando bem funcional. Resolvi passar um `try catch` dentro do `handleSubmit` para que caso algum erro ocorresse fosse renderizado na tela um aviso de erro, definido aqui como um genérico *'Invalid IP address or Domain!'*. Vale ressaltar que no layout disponibilizado pelo FrontEndMentor não há um estado para exibir erros ou algo assim, então decidi por exibir esse erro dentro de um `<span>` dentro do componente `SearchBar`. Esse `<span>` então recebeu a propriedade `position: absolute` para ficar logo acima do container da *SearchBar* , que passou a ter `position: relative`. Assim caso um erro surja ao realizar uma pesquisa, ele será exibido logo acima da barra de 
pesquisa em um vermelho de baixa saturação. 

Bem, esse é apenas meu primeiro texto e a ideia é que ele 
sirva como um diário para que eu possa medir minha evolução. De forma alguma esse texto é para ser encarado como um tutorial ou algo semelhante, é apenas um registro de como eu penso para resolver os problemas hoje.

Github do projeto: https://github.com/GuihLeme/ip-address-tracker
Site: https://ip-address-tracker-taupe-six.vercel.app/