Utilizando o ModalRoute e fazendo upload de imagens
ReactJS • 23 de Ago de 2018
Esse post é a decima 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 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
Lá vamos nós novamente, hoje vamos aprender a adicionar novos imóveis no mapa. Lembrando que até aqui já temos a área de SignIn e SignOut, bem como o mapa com a lista de imóveis. Agora sem mais lenga-lenga, boooora para o código.
Para adicionar um novo imóvel, será preciso adicionar um botão junto ao do logout e quando ele for clicado exibir um Map Marker e as opções de Adicionar e Cancelar. Neste caso, o Map Marker ficará fixo, e o usuário movimentará o mapa até achar a posição (latitude e longitude) para adicionar seu imóvel.
A principio será criado um PointReference para que o Map Marker seja fixado, então no arquivo, App/styles.js:
// Importe o polished
import { darken } from "polished";
export const PointReference = styled.div`
color: #fc6963;
-webkit-text-fill-color: #fc6963;
-webkit-text-stroke-color: ${() => darken(0.05, "#fc6963")};
background-color: #fc6963;
Esse PointReference cria uma layer que ficará sobre o Mapbox, aqui foi utilizado o FontAwesome para ser o Map Marker.
Agora é preciso importar o PointReference e fazer algumas alterações, para exibir ele somente quando o Button para adicionar imóvel for clicado, e o interessante é que nesse momento haja a listagem de imóveis, deixando assim o mapa mais limpo. No App/index.js:
// Adicionar o PointReference
import { Container, ButtonContainer, PointReference } from "./styles";
viewport: // ...
// Novo state
addActivate: false
color="#fc6963"
onClick={() => this.setState({ addActivate: true })}
<i className="fa fa-plus" />
<Button color="#222" onClick={this.handleLogout}>
<i className="fa fa-times" />
Ainda falta exibir o render do PointReference e também tirar a lista de imóveis do mapa.
// Novo render
renderButtonAdd() {
this.state.addActivate && (
<PointReference>
<i className="fa fa-map-marker" />
<button onClick={this.handleAddProperty} type="button">
onClick={() => this.setState({ addActivate: false })}
className="cancel"
</PointReference>
const { containerWidth: width, containerHeight: height, match } = this.props;
const { properties, addActivate } = this.state;
{!addActivate && <Properties match={match} properties={properties} />}
{this.renderButtonAdd() /* Esse aqui será adicionado */}
Repare que só aparecerá as Properties quando o addActivate for false, e para não poluir o render foi criada um novo método que adiciona o PointReference caso o addActivate seja true.
E para finalizar essa parte, quando o usuário clicar em adicionar deve ser redirecionado para uma nova página com a latitude e longitudepresente para adicionar as demais informações sobre o imóvel.
handleAddProperty = () => {
const { match, history } = this.props;
const { latitude, longitude } = this.state.viewport;
history.push(
`${match.url}/properties/add?latitude=${latitude}&longitude=${longitude}`
this.setState({ addActivate: false });
A página que o usuário será redirecionado ainda não foi criada (já já será), ela será uma página Modal igual existe em aplicativos como Instagram e Facebook, onde você não precisa carregar uma nova página quando clica no link, mas com a url você consegue chegar na mesma.
Agora vamos começar a trabalhar em cima de uma nova página para adicionar um imóvel, essa página será uma página Modal e ela ficará dentro da página App. A ideia de abrir uma página modal é ter uma url exclusiva para ela mas que é "fácil" voltar para a página principal.
Para trabalhar com o Modal utilize o react-router-modal, e além de instalar ele adicione também o seguinte:
npm install react-router-modal react-dropzone stringquery classnames
react-router-modal: Trabalhar com o modal junto com o react-router-dom.
react-dropzone: Permite jogar arquivos selecionados numa determinada área do browser
stringquery: Ajudará a trabalhar com a query de uma url.
classnames: Adiciona classes CSS através de objetos.
Agora que você instalou as dependências, é hora de usa-las.
No routes.js, você deve configurar o seguinte:
import "react-router-modal/css/react-router-modal.css";
Isso serviu para por o ModalContainer nas rotas, ele será importante para utilização do Modal.
Agora é preciso criar uma nova página que adicionará o imóvel, ela conterá apenas um formulário e através da url receberá a latitude e a longitude. Então monte aquela estrutura marota de páginas:
|--- AddProperty/
|--- styles.js
Depois da estrutura montada, já comece estilizando com o seguinte código no styles.js:
export const File = styled(Dropzone)`
border: 2px dashed #ff3333;
&.without-files {
Aqui você percebe o StyledComponents (s2) trabalhando de forma mágica, mais uma vez. Dessa vez não foi utilizado um elemento do styled e sim um outro componente (Dropzone), isso foi possível porque o Dropzone é um elemento React, mas foi só chama-lo como parâmetro do styled que foi sucesso!!!
Ainda no styles.js será necessário estilizar o form, mas devido ao código está muito extenso copie desse link aqui. Só não vai me esquecer de copiar rsrs.
Agora é hora da página, então no index.js comece importando alguns componentes:
import querySearch from "stringquery";
import { Form, File } from "./styles";
Na class além dos states será validado no componenteDidMount se na query da url os parâmetros latitude e longitude estão presentes, caso não esteja, ele enviar o usuário para a página do App. Então ainda no index.js:
class AddProperty extends Component {
push: PropTypes.func
const params = querySearch(this.props.location.search);
!params.hasOwnProperty("latitude") ||
!params.hasOwnProperty("longitude")
alert("É necessário definir a latitude e longitude para um imóvel.");
this.props.history.push("/app");
this.setState({ ...params });
// ainda tem mais
Lá no styles o componente Dropzone foi importado, lembra? Ele é o componente em que separa uma área na página e dentro dela o usuário pode adicionar arquivos arrastando de pastas externas ao Browser e jogando dentro dela ou mesmo clicar para abrir o gerenciador de arquivos.
Os próximos métodos trabalham com o Dropzone, um para colocar os arquivos no state e o outro para renderizar esses arquivos.
handleDrop = files => this.setState({ files });
renderFiles() {
const { files } = this.state;
return !files.length ? (
<p>Jogue as imagens ou clique aqui para adiciona-las</p>
files.map(file => <img key={file.name} src={file.preview} />)
Agora será trabalhado com o evento do handlerSubmit que é disparado no onSubmit, para ficar mais claro foi dividido em duas partes:
const { title, address, price, latitude, longitude, files } = this.state;
if (!title || !address || !price || !latitude || !longitude) {
this.setState({ error: "Preencha todos os campos" });
const { data: { id } } = await api.post("/properties", {
// tem mais
As considerações sobre a primeira parte são as seguintes, será utilizado métodos assíncronos e por isso o uso do async/await e já foi removido o envio padrão do formulário para uma action com o e.preventDefault().
Dentro try/catch é trabalhado com as requisições e para elas foram extraídas do state as variáveis importantes e tão logo verifica se estão presentes para dar continuidade a função.
É importante notar que o já foi extraído o id, através da desestruturação, da resposta da requisição da API para criar o imóvel.
A segunda parte desse método, não tem segredo, bora disseca-la:
// Continuação do handleSubmit
if (!files.length) this.props.history.push("/app");
files.map((file, index) =>
data.append(`image[${index}]`, file, file.name)
await api.post(
`/properties/${id}/images`,
this.setState({ error: "Ocorreu algum erro ao adicionar o imóvel" });
Ainda dentro do try/catch nessa segunda parte temos o seguinte, uma verificação se existe algum arquivo (files) adicionado ao state, não existindo o usuário será enviado para a página App.
Todavia, existindo arquivos, foi utilizado o FormData para estrutura-los de maneira que um formulário deve está. Para essa requisição, por estar trabalhando com um formulário de arquivos, o cabeçalho deve conter o content-type como multipart/form-data. Feita a requisição à API, para adicionar os arquivos ao id definido anteriormente o usuário é enviado para a página de App.
Por fim, caso exista algum problema nas requisições um erro no formulário é apresentado ao usuário.
Ainda dentro do index.js, o método handleCancel deve ser adicionado:
Esse método apenas mandará o usuário para a pagina App.
E para finalizar a página AddProperty, você deve adicionar o render:
<h1>Adicionar imóvel</h1>
placeholder="Título"
placeholder="Endereço"
onChange={e => this.setState({ address: e.target.value })}
placeholder="Preço"
onChange={e => this.setState({ price: e.target.value })}
className={classNames({ "without-files": !this.state.files.length })}
{this.renderFiles()}
<button type="submit">Adicionar</button>
<button onClick={this.handleCancel} className="cancel">
export default withRouter(AddProperty);
Aqui é simplesmente um formulário utilizando os componentes previamente estilizados. Talvez você se pergunte sobre esse trecho de código: classNames({ "without-files": !this.state.files.length }), nele a função classNames serve para trabalhar com classes do CSS a partir de um objeto. O classNames funciona assim, caso o valor da chame seja true ele entende que aquela classe deve ser adicionado ao className.
Chamando a página
Agora você está a um passo de concluir esse Post. Falta ainda chamar essa página que acabamos de criar em algum lugar.
Ela será chamada dentro da página App, já que é um Modal da página é lá que o AddProperty deve ficar. Então no App/index.js comece importando algumas coisas:
import { ModalRoute } from "react-router-modal";
import AddProperty from "../AddProperty";
O ModalRoute fará o papel do Route definindo o path e o component e como o nome sugere será para o Modal.
Agora dentro do render do componente App, adicione:
{this.renderButtonAdd()}
<ModalRoute
path={`${match.url}/properties/add`}
parentPath={match.url}
component={AddProperty}
No ModalRoute o path é a url que executará o component que é a página AddProperty, já o parentPath identifica qual é a página que chamou o Model e quando ele for fechado, por exemplo, retorna para a página do parentPath.
É meu caro essa é a pior parte da brincadeira, mas fica tranquilo que ainda tem mais!!!
Hoje você aprendeu a utilizar RouteModal e já consegue adicionar o imóvel pelo APP. Irado, não?
Espero que você tenha gostado desse Post, caso tenha alguma dúvida deixe aí nos comentários!! E pode compartilhar ele como se não tivesse amanhã!!!!