Esse post é a decima primeira 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.

Estou aqui mais um dia, sobre o olhar atento dos Devs que programam. Não sou muito bom de rima rs.
Bom, hoje finalizaremos a série do AirBnB e para finaliza-la precisamos apenas exibir as informações das propriedades que estão no mapa. Não tem segredo, agora é só utilizar o que já temos, o ModalRoute, StyledComponent e por ai vai.

Página do imóvel

Assim como a página para adicionar um imóvel, na página para exibir informações dele, teremos uma página modal. Nela será exibida as demais informações, como título, endereço e imagens.

Quando falo de página, você já pensa naquela velha e boa estrutura já conhecida:

src/pages/
      |--- Property/
              |--- index.js
              |--- styles.js

No styles.js já defina os componentes com seus respectivos layouts:

import styled from "styled-components";

export const Images = styled.div`
  width: 100%;
  max-width: 660px;
  font-size: 16px;
  color: #777777;
  text-align: center;
  display: grid;
  grid-template-columns: 100px 100px 100px;
  grid-gap: 5px;
  background-color: #fff;
  color: #444;
  &.without-images {
    display: flex;
  }
  img {
    width: 100px;
  }
  p {
    margin-top: 15px;
    border: none !important;
  }
`;

export const Container = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px 20px;
  p {
    margin-bottom: 15px;
    padding: 10px;
    width: 100%;
  }
  hr {
    margin: 20px 0;
    border: none;
    border-bottom: 1px solid #cdcdcd;
    width: 100%;
  }
  span {
    color: #fc6963;
    font-size: 20px;
  }
`;

O Images será o container das imagens, que é um grid de 3 colunas, já Container engloba todo o conteúdo do Modal com suas respectivas estilizações.

No index.js é necessário fazer os imports:

import React, { Component, Fragment } from "react";
import { Container, Images } from "./styles";
import PropTypes from "prop-types";

import api from "../../services/api";

const intlMonetary = new Intl.NumberFormat("pt-BR", {
  style: "currency",
  currency: "BRL",
  minimumFractionDigits: 2
});

Além dos imports, já foi configurado o objeto Intl.NumberFormat para transformar o price em reais.

Agora será criado o componente, definido o propTypes e alguns states. Será definido também o que acontecerá no método componentDidMount:

export default class Property extends Component {
  static propTypes = {
    match: PropTypes.shape({
      params: PropTypes.shape({
        id: PropTypes.string
      })
    }).isRequired
  }
  state = {
    property: null,
    loading: false
  };

  async componentDidMount() {
    try {
      const { id } = this.props.match.params;
      this.setState({ loading: true });

      const { data } = await api.get(`/properties/${id}`);
      this.setState({ property: data });
    } catch (err) {
      console.log(err);
    } finally {
      this.setState({ loading: false });
    }
  }
  // Mais

O componentDidMount mais uma vez foi definido como assíncrono e dentro dele já inicia com o try/catch. Foi resgatado dos parâmetros da url o id do imóvel, esse id será usado pegar as informações do imóvel lá na API.  Repare que no state foi adicionado o loading como true e caso consiga resgatar da API o property também é adicionado.

O catch pegará algum erro, caso aconteça. Não é preciso jogar nada no state pois se o property for null um erro será apresentado ao renderizar o imóvel.
O finally será chamado quando tudo der certo ou quando tudo der errado, para finalizar o loading.

Ainda no index.js:

 // ...
  renderProperty() {
    const { property } = this.state;

    if (!property) {
      return "Imóvel não encontrado!";
    }

    return (
      <Fragment>
        <h1>{property.title}</h1>
        <hr />
        <p>{property.address}</p>
        <Images>
          {property.images.map(image => (
            <img src={image.url} alt={image.path} />
          ))}
        </Images>
        <span>{intlMonetary.format(property.price)}</span>
      </Fragment>
    );
  }
  // Mais

Por questão de organização e limpeza do código o render do imóvel foi separado em um método, onde tem uma pequena verificação exibindo que ele não foi encontrado caso ocorra algum erro ou exibindo a estrutura de informações do imóvel caso tudo ocorra bem.

Por fim, adicione o render:

// ...
render() {
    const { loading } = this.state;
    return (
      <Container>
        {loading ? <p>Carregando</p> : this.renderProperty()}
      </Container>
    );
  }
}

No render com o operador ternário exibe a mensagem “Carregando” ou o método de renderização do imóvel.

Exibindo a página

Agora resta você adicionar essa página lá no App, seguindo a mesma ideia da página AddProperty e também ajustar o Link lá no componente Properties para chamar a nova página. No App/index.js:

// ...
import Property from "../Property";

// ...
  render() {
    // ...
    // Depois de 
    <ModalRoute
      path={`${match.url}/properties/add`}
      parentPath={match.url}
      component={AddProperty}
    />
    // Adicione
    <ModalRoute
      path={`${match.url}/property/:id`}
      parentPath={match.url}
      component={Property}
    />
  }
// ...

Já finalizando, lá em App/components/Properties/index.js:

// De
const Properties = ({ properties }) =>
// Para
const Properties = ({ properties, match }) =>

// ...
<Pin>
  // De
  <Link to="">{intlMonetary.format(property.price)}</Link>
  // Para
  <Link to={`${match.url}/property/${property.id}`}>
    {intlMonetary.format(property.price)}
  </Link>
 </Pin>
// ...

Properties.propTypes = {
  properties: ...,
  // Adicione
  match: PropTypes.shape({
    url: PropTypes.string
  }).isRequired
}

E teremos isso:

Goodbye

Devs mais queridos do Brasil, por hoje é isso!!
A série do AirBnB pode ser considerada finalizada agora que já exibe as informações do imóvel.

Fica aqui nossa salva de palmas para todos que participaram dessa série e esperamos que você tenha gostado * — *

Código aqui está aqui.

Ah, não se esqueça de deixar seu comentário e compartilhar com seus amigos devs.

Abraços