Instalando o Mapbox e listando imóveis no React Native
Esse post é a quinta parte da série de posts “Clone AirBnB com AdonisJS, React Native e ReactJS” onde iremos construir do zero uma aplicação web com ReactJS e também uma aplicação mobile com React Native com dados servidos através de uma API REST feita com NodeJS utilizando o framework AdonisJS.
- Parte 1: Iniciando com AdonisJS: Autenticação JWT e API REST;
- Parte 2: Criando CRUD e relações em API REST no AdonisJS;
- Parte 3: Upload de imagens e geolocalização no AdonisJS;
- Parte 4: Iniciando com React Native: Navegação e Autenticação com JWT;
- Parte 5: Instalando o Mapbox e listando imóveis no React Native;
- Parte 6: Instalando a Câmera e realizando o cadastro de Imóveis;
- Parte 7: Listando em um Modal os dados detalhados dos Imóveis;
- Parte 8: Iniciando com ReactJS: Navegação e Autenticação com JWT;
- Parte 9: Instalando o Mapbox e listando os imóveis no ReactJS;
- Parte 10: Utilizando o ModalRoute e fazendo upload de imagens;
- Parte 11: Exibindo informações do imóvel com ModalRoute;
Nesse ponto já temos a aplicação criada e configurada para se comunicar com a API desenvolvida no começo dessa série, e com isso já conseguimos implementar o processo de autenticação e cadastro de usuário, nessa parte iremos instalar e configurar o Mapbox para podermos fazer o uso de mapas e também buscar da API uma listagem de imóveis cadastrados com base na distância do ponto onde você está.
Iniciando a Aplicação
Caso você tenha entrado direto nessa parte, você pode acessar esse link e baixar o projeto com todas as configurações do post anterior, apesar de eu recomendar pessoalmente que você leia a primeira parte para entender o fluxo da aplicação.
Ou se você já viu a primeira parte pode continuar para a próxima sessão onde iremos começar a instalação do Mapbox.
Instalando e Configurando o Mapbox
Para não deixar o post muito extenso explicando o passo a passo da instalação e configuração do Mapbox, você pode correr no Blog da Rocketseat e ler esse super post do Higo sobre a instalação e configuração do Mapbox.
E para obter o token de acesso indicado no post sobre o Mapbox você pode acessar esse link, use o botão Sign In para fazer login com sua conta ou então se cadastrar, depois de logado basta acessar esse link e ter acesso à seu token 😉
Caso você tenha algum problema na instalação ou não tenha conseguido fazer todas as configurações acesse esse link e baixe a estrutura configurada para dar continuidade.
Usando o Mapbox na Tela Principal
Até no post anterior quando um usuário era autenticado com sucesso na aplicação o código que tínhamos na Tela Principal
era:
import React from 'react';
import { View } from 'react-native';
// import styles from './styles';
const Main = () => (
<View />
);
export default Main;
Ainda tem dúvidas ou insegurança com Javascript usando a sintaxe do ES6? Tá esperando o que? Corre lá no site da Rocketseat (entre aqui) e faça os nossos cursos gratuitos sobre Javascript e ES6/ES7/ES8.
Ufa, agora sim vamos inserir o Mapa ocupando a tela toda, para isso primeiro adicione a linha de importação do componente MapboxGL, como abaixo:
import MapboxGL from '@mapbox/react-native-mapbox-gl';
Feito isso troque a View que temos dentro do componente App por:
<MapboxGL.MapView
centerCoordinate={[-49.6446024, -27.2108001]}
style={{ flex: 1 }}
styleURL={MapboxGL.StyleURL.Dark}
/>
O código acima irá inserir na Tela Principal o Mapa ocupando todo espaço disponível, as props passadas para ele são: centerCoordinate, que é usada para indicar qual o ponto que o mapa deve inicializar centralizado e a styleURL que é usada para dar o estilo ao mapa, no nosso caso, um tema escuro.
Obs.: Se você ainda não adicionou o token de acesso do Mapbox em lugar algum, abra o arquivo index.js
da pasta src
e adicione a importação do Mapbox logo no início seguida da linha abaixo, do contrário o mapa será inserido na tela mas não será exibido.
MapboxGL.setAccessToken('<aqui_vai_seu_token>');
Nesse momento se você executar o aplicativo no emulador/simulador a tela principal deve estar assim:
Para finalizar a Tela Principal falta listar os imóveis cadastrados no banco de dados e também adicionar alguns detalhes ao código, como esconder a navigationBar e modificar a cor da StatusBar.
Primeiramente vamos criar no arquivo styles.js
um Container usando a lib Styled Components, o estilo vai ficar assim:
import styled from 'styled-components';
const Container = styled.View`
flex: 1;
`;
export { Container };
Agora vamos importá-lo no arquivo index.js
como já fizemos anteriormente nas outras telas e inseri-lo por volta do componente de Mapa, e para mudar a cor da StatusBar adicione a importação dela no começo do código, dessa maneira:
import { StatusBar } from 'react-native';
E dentro do Container mas acima do componente de Mapa adicione a StatusBar com a prop barStyle="light-content"
. O código final do index.js
deve ficar assim:
import React from 'react';
import PropTypes from 'prop-types';
import { StatusBar } from 'react-native';
import MapboxGL from '@mapbox/react-native-mapbox-gl';
import { Container } from './styles';
const Main = () => (
<Container>
<StatusBar barStyle="light-content" />
<MapboxGL.MapView
centerCoordinate={[-49.6446024, -27.2108001]}
style={{ flex: 1 }}
styleURL={MapboxGL.StyleURL.Dark}
/>
</Container>
);
Main.navigationOptions = {
header: null,
};
export default Main;
Você pode ver o código completo da aplicação até esse ponto nesse link.
Buscando a Lista de Imóveis na API
Com o mapa sendo renderizado corretamente vamos agora recuperar a lista de imóveis da API para listá-los no Mapa.
O primeiro passo para essa funcionalidade é fazer uma pequena mudança no componente Main, que para realizar essa busca de imóveis precisaremos fazer uso dos métodos de Ciclo de Vida desse componente, portanto precisamos transformá-lo de um Stateless Component (baseado em constante) para um Stateful Component (baseado em classe), o código do componente transformado fica assim:
export default class Main extends Component {
static navigationOptions = {
header: null,
}
render() {
return (
<Container>
<StatusBar barStyle="light-content" />
<MapboxGL.MapView
centerCoordinate={[-49.6446024, -27.2108001]}
style={{ flex: 1 }}
styleURL={MapboxGL.StyleURL.Dark}
/>
</Container>
);
}
}
Com o componente transformado em Stateful Component podemos criar a função que será a responsável por renderizar no mapa a lista de Imóveis que serão buscados da API, logo abaixo função que usaremos para isso, mas antes dela devemos fazer 2 ajustes para que ela funcione corretamente, o primeiro é criar um state no componente contendo a variável locations contendo um array vazio, ficando assim:
state = {
locations: [],
}
O próximo ajuste é dentro do componente de mapa, devemos ajustá-lo para receber componentes dentro dele, para isso você pode deixá-lo assim:
<MapboxGL.MapView
centerCoordinate={[-49.6446024, -27.2108001]}
style={{ flex: 1 }}
styleURL={MapboxGL.StyleURL.Dark}
>
{ this.renderLocations() }
</MapboxGL.MapView>
Agora sim podemos focar na criação da função renderLocations, que irá apenas fazer um map na variável locations do state e para cada item renderizar uma PointAnnotation no Mapbox, seu código ficou assim:
renderLocations = () => (
this.state.locations.map(location => (
<MapboxGL.PointAnnotation
id={location.id.toString()}
coordinate={[parseFloat(location.longitude), parseFloat(location.latitude)]}
>
<AnnotationContainer>
<AnnotationText>{location.price}</AnnotationText>
</AnnotationContainer>
<MapboxGL.Callout title={location.title}/>
</MapboxGL.PointAnnotation>
))
)
E para que os pontos sejam renderizados corretamente e não haja erro de importação, vamos primeiro no arquivo styles.js
e criar os componentes AnnotationContainer e AnnotationText, ficando assim:
import styled from 'styled-components';
const Container = styled.View`
flex: 1;
`;
const AnnotationContainer = styled.View`
alignItems: center;
justifyContent: center;
backgroundColor: #FC6663;
borderRadius: 5;
padding: 5px;
`;
const AnnotationText = styled.Text`
fontSize: 14px;
color: #FFF;
`;
export { Container, AnnotationContainer, AnnotationText };
E depois importá-los no index.js
da Tela Principal, ficando assim:
import {
Container,
AnnotationContainer,
AnnotationText
} from './styles';
Estamos quase terminando essa parte de listagens de imóveis(aleluiaaaaa), temos apenas que adicionar mais um método no arquivo index.js
pra terminarmos a busca de imóveis da API, afinal já configuramos para listar os imóveis mas ainda não enviamos a requisição que irá retornar essa lista rsrs.
Para finalizar vamos criar o método componentDidMount no componente Main, ele será o responsável por enviar a requisição para a API assim que o componente for montado, irei postar o código e logo abaixo explico o que está sendo feito:
async componentDidMount() {
try {
const response = await api.get('/properties', {
params: {
latitude: -27.210768,
longitude: -49.644018,
},
});
this.setState({ locations: response.data });
} catch (err) {
console.tron.log(err);
}
}
Você deve ter percebido logo no início da função o async, isso porque esse é o único método do ciclo de vida que pode ser executado de forma assíncrona, dentro dele temos um try/catch para tratativa de erros, e no começo do try já temos a função responsável por enviar a requisição para a API, e como visto nos posts sobre a API para recuperar os imóveis temos que passar as coordenadas do usuário para que sejam trazidos baseado na distância, assim que houver uma resposta da requisição o array é passado para o locations do state.
Bom nesse ponto se tudo deu certo quando você tentar enviar a requisição para a API haverá um erro (Ué, mas como haverá erro se tudo está correto?).
Esse erro acontece pois depois que o usuário foi autenticado temos que enviar o Token JWT em todas as requisições para validar o usuário e retornar um resultado correto, mas não se preocupe, veja a sessão abaixo que vamos corrigir isso (acho bom mesmo).
Implementando Interceptors no Axios para envio do Token JWT
Para garantir que o Axios envie em toda requisição o token JWT no índice Authentication do Header da mesma vamos implementar o recurso de Interceptors.
Você pode ler mais sobre esse recurso diretamente na documentação da lib nesse link.
Para implementar é bem simples, abram o arquivo de configuração do Axios, o api.js
, e entre o const api = axios.create();
e o export default api;
o seguinte código:
api.interceptors.request.use(async (config) => {
try {
const token = await AsyncStorage.getItem('@AirBnbApp:token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
} catch (err) {
alert(err);
}
});
Esse trecho de código irá interceptar todas as requisições enviadas e antes de repassá-la para a API vai verificar se há no Async Storage o token que nós criamos no momento do Login, caso ele exista será adicionado no Authorization do Header esse Token no formato correto para a API reconhecer e validá-lo, e por último será retornado essa nova config para a request.
Ufa, mais uma parte chega ao fim
Nessa parte da série vimos como utilizar o token retornado da autenticação para buscarmos informações que a API só fornece para um usuário depois de autenticado.
Se você gostou desse post e também da série sobre o AirBnb deixe seu comentário, crítica ou sugestão que é muito importante pra gente.
Aguardo você no próximo post 😉