Escolha uma Página
Como criar uma aplicação desktop, com React Js & Electron.

Como criar uma aplicação desktop, com React Js & Electron.

O Electron se destacou muito nos últimos tempos por oferecer a possibilidade de construirmos aplicações desktop utilizando tecnologias web.

As aplicações construídas com Electron podem funcionar Offline, acessar os Recursos Nativos do Sistema Operacional e até mesmo serem comercializadas nas lojas de aplicativos.

Hoje, vamos aprender a desenvolver uma aplicação desktop completa, com Electron e React Js.

Passo #1: Criação do projeto

Para começamos, criaremos um projeto através do Create React App.

Você pode criar seu projeto através do comando abaixo:

create-react-app app

Com o processo finalizado, você pode entrar na pasta app que você criou e rodar o comando:

yarn start

Após alguns segundos, uma nova janela no seu navegador se abrirá, semelhante a essa:

Tela padrão de projetos criados através do CRA.

Passo #2: Instalação das bibliotecas

Agora, instalaremos as bibliotecas que usaremos no nosso projeto.

Começaremos instalando o electron, o wait-on e a biblioteca concurrently como dependências de desenvolvimento.

Para isso, dentro da pasta do seu projeto, rode o comando abaixo:

yarn add electron wait-on concurrently -D

O concurrently é biblioteca que nos permitirá rodar vários comandos em sequência.

O wait-on nos permitirá esperar até que a página web da nossa aplicação esteja carregada, para assim, iniciarmos nosso app.

Também precisamos instalar a biblioteca electron-is-dev como uma dependência de produção.

Você pode fazer isso com o comando abaixo:

yarn add electron-is-dev

Instalaremos algumas dependências extras no Passo #6: Build do Projeto.


Passo #3: Configuração do Electron

Agora, precisamos fazer algumas configurações para que o Electron funcione corretamente.

Para isso, crie um arquivo electron.js na pasta public do seu projeto.

Esse arquivo precisa conter o seguinte conteúdo:

const electron = require('electron');

const { app } = electron;
const { BrowserWindow } = electron;

const path = require('path');
const isDev = require('electron-is-dev');

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
    },
  });

  mainWindow.loadURL(
    isDev ? 'http://localhost:3000' : `file://${path.resolve(__dirname, '..', 'build', 'index.html')}`,
  );

  if (isDev) {
    mainWindow.webContents.openDevTools();
  }

  mainWindow.on('closed', () => {
    mainWindow = null;
  });
}

app.on('ready', createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  if (mainWindow === null) {
    createWindow();
  }
});

A função createWindow é responsável por criar uma janela no desktop, chamada BrowserWindow.

O BrowserWindow pode receber alguns parâmetros, como a largura (width) e a altura (height) em que a janela iniciará.

A opção nodeIntegration: true nos permite acesso à API nativa do electron, é importante que essa opção esteja ativada para a aplicação que vamos construir nesse post.

Agora, precisamos apenas criar o nosso script de desenvolvimento, para isso, adicione o script abaixo no campo scripts do seu package.json:

"dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron public/electron.js\""

Nesse momento, o electron está configurado e pronto para uso.

Sua estrutura de pastas deve estar semelhante a essa:

Para ver aplicação em modo de desenvolvimento, basta rodar o comando abaixo:

yarn dev

Ao rodar o comando acima, uma nova janela será aberta na sua máquina, semelhante a essa:


Passo #4: Configurações gerais da aplicação

Para criar o projeto desse post, você também precisará instalar o syled-components, para isso, basta rodar o comando abaixo:

yarn add styled-components

Em todos os projetos que eu crio utilizando Create React App, eu sempre gosto de fazer algumas configurações para deixar o projeto mais organizado.

Começaremos apagando alguns arquivos na pasta src, são eles:

  • App.css
  • App.test.js
  • index.css
  • logo.svg
  • serviceWorker.js

Apague também os seguintes arquivos da pasta public:

  • logo192.png
  • logo512.png
  • manifest.json
  • robots.txt

Precisamos também remover a importação desses arquivos.

O seu index.js da pasta src deve ficar da seguinte forma:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

E o seu arquivo App.js da pasta src deve ficar da seguinte forma:

import React from 'react';

function App() {
  return <div />;
}

export default App;

Nesse momento, sua estrutura deve estar mais ou menos assim:


Passo #5: Construção da aplicação

Agora, iremos dar início a construção da nossa aplicação.

Criaremos uma Todo List, semelhante ao Google Keep.

Para isso, crie uma pasta pages dentro da pasta src do seu projeto.

Dentro da pasta pages, crie uma nova pasta Main, e nela, crie dois novos arquivos, o index.js e o styles.js

No arquivo index.js, adicione o seguinte conteúdo:

import React, { useState, useEffect } from 'react';
import crypto from 'crypto';

import {
  Container,
  NewTodo,
  NewTodoInput,
  TodosList,
  Todo,
  TodoName,
  Button,
  AddTodoMessage,
} from './styles';

const { ipcRenderer } = window.require('electron');

export default function Main() {
  const [todos, setTodos] = useState([]);

  function handleNewTodo(e) {
    e.preventDefault();
    const newTodo = e.target.todo.value;

    if (!newTodo) return;

    const todo = {
      id: crypto.randomBytes(256),
      name: newTodo,
    };

    setTodos([...todos, todo]);

    const message = {
      title: 'Um novo todo foi criado',
      body: `O todo ${todo.name} foi criado`,
    };

    ipcRenderer.send('@notification/REQUEST', message);

    e.target.reset();
  }

  function handleRemoveTodo(todoId) {
    setTodos(todos.filter(todo => todo.id !== todoId));
  }

  function renderTodos() {
    return todos.map(todo => (
      <Todo key={todo.id}>
        <TodoName>{todo.name}</TodoName>

        <Button onClick={() => handleRemoveTodo(todo.id)}>Concluir</Button>
      </Todo>
    ));
  }

  function handleNotificationFailure(message) {
    console.log(message);
  }

  useEffect(() => {
    ipcRenderer.on('@notification/FAILURE', handleNotificationFailure);

    return () => {
      ipcRenderer.removeListener('@notification/FAILURE', handleNotificationFailure);
    };
  }, []);

  return (
    <Container>
      <NewTodo onSubmit={handleNewTodo}>
        <NewTodoInput name="todo" placeholder="Novo todo" />
        <Button>Adicionar</Button>
      </NewTodo>
      {todos.length > 0 ? (
        <TodosList>{renderTodos()}</TodosList>
      ) : (
        <AddTodoMessage>Adicione um novo todo :D</AddTodoMessage>
      )}
    </Container>
  );
}

Nesse arquivo, criamos uma página de criação de todos, que salva os todos em uma variável, através do state.

A função handleNewTodo é responsável por criar um novo todo na nossa lista atual, e em seguida, envia uma mensagem à API Nativa do Electron.

Essa mensagem contém um tipo (@notification/REQUEST) e os detalhes dessa mensagem, contidos na variável message.

Utilizamos o useEffect para definirmos um listener caso a criação da mensagem falhe, e nesse caso, chamamos a função handleNotificationFailure onde damos um console na mensagem de erro.

Temos também a função handleRemoveTodo que remove o todo quando o usuário clica em Concluir.

Agora, criaremos os estilos dessa página, utilizando o styled-components.

Para isso, no arquivo styles.js, adicione o seguinte conteúdo:

import styled from 'styled-components';

export const Container = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
`;

export const NewTodo = styled.form`
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  margin: 0 auto;
  padding: 50px 0;
  text-align: center;
`;

export const NewTodoInput = styled.input`
  height: 50px;
  padding: 0 15px;
  margin: 10px;
  border: 0.5px solid #333;
  border-radius: 4px;
  font-size: 14px;
`;

export const TodosList = styled.div`
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  list-style: none;
  margin-top: 50px;
`;

export const Todo = styled.div`
  width: 250px;
  max-width: 250px;
  border: 1px solid #eee;
  padding: 15px 20px;
  text-align: left;
  margin: 10px;
`;

export const TodoName = styled.h3`
  font-size: 16px;
  margin-bottom: 5px;
  color: #333;
`;

export const Button = styled.button`
  width: 100%;
  max-width: 250px;
  height: 50px;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.05);
  border: 0;
  border-radius: 4px;
  background: #009cde;
  color: #fff;
  font-weight: bold;
  cursor: pointer;
`;

export const AddTodoMessage = styled.h1`
  color: #333;
`;

Com os estilos da nossa página criados, criaremos agora o código de exibição de uma mensagem nativa quando um todo for criado.

Nesse código, faremos com que seja exibido uma notificação sempre que um novo todo for criado.

Notificação no MacOS

Para isso, começaremos a utilizar a API Nativa que o Electron nos oferece.

Começaremos criando uma pasta chamada app na raiz do projeto (fora da pasta src).

Dentro da pasta app, crie um arquivo index.js

No arquivo index.js, adicione o seguinte conteúdo:

const { ipcMain, Notification } = require('electron');

ipcMain.on('@notification/REQUEST', async (event, message) => {
  try {
    const { title, body } = message;

    const notification = new Notification({
      title,
      body,
    });

    notification.show();
  } catch (err) {
    event.sender.send('@notification/FAILURE', 'Houve um erro na criação da notificação');
  }
});

Essa parte do projeto segue bastante a ideia do websocket, onde enviamos uma mensagem sempre que quisermos nos comunicar com a API Nativa do Electron.

Nesse arquivo, nós estamos ouvindo a mensagem do tipo @notification/REQUEST, e esperamos receber o parâmetro message.

O event é enviado por padrão para todos os listeners, contendo as informações do evento e de quem o disparou.

Ao receber a mensagem do tipo que definimos (@notification/REQUEST), nós instanciamos um objeto do tipo Notification e utilizamos o método show para mostrar a mensagem em tela.

No catch dessa função, nós enviamos uma mensagem para sender informando que houve um erro.

Agora, precisamos importar esse arquivo dentro do arquivo public/electron.js que criamos no Passo #3: Configuração do Electron.

Para isso, adicione a seguinte linha logo após o require do electron:

require('../app');

Precisamos importar também a página Main que criamos no Passo #5: Construção da aplicação.

Para isso, deixe o conteúdo do arquivo App.js da pasta src da seguinte maneira:

import React from 'react';

import Main from './pages/Main';

function App() {
  return (
    <>
      <Main />
    </>
  );
}

export default App;

Nesse momento, sua estrutura de pastas deve estar semelhante a essa:

OBS.: É importante que você pare seu servidor o rode o comando yarn dev sempre que houver alguma alteração nos arquivos relacionados ao Electron.

Demonstração


Passo #6: Build do Projeto

Para fazer o build do projeto precisamos seguir alguns passos importantes.

Primeiramente, precisamos instalar as dependências necessárias para que o build do projeto seja feito corretamente.

Para instalar as bibliotecas, utilize o comando abaixo:

yarn add @rescripts/cli @rescripts/rescript-env electron-builder typescript -D

Precisaremos também fazer algumas alterações nas configurações do webpack do nosso projeto.

Para isso, utilizaremos a biblioteca rescripts para fazermos essas alterações sem precisar ejetar as configurações do Create React App.

O electron-builder é a biblioteca sugerida pelo próprio electron para o build de projetos.

Para começarmos a fazer essas configurações, crie um arquivo na raiz do seu projeto chamado .webpack.config.js e adicione o seguinte conteúdo:

module.exports = config => {
  config.target = "electron-renderer";
  return config;
};

Em seguida, precisamos fazer o rescripts entender essas configurações.

Para isso, também na raiz do seu projeto, crie um arquivo .rescriptsrc.js e adicione à ele o seguinte conteúdo:

module.exports = [require.resolve("./.webpack.config.js")];

Agora, precisamos definir algumas informações referentes ao criador do software.

Essas informações são muito importantes, principalmente se você quiser disponibilizar seu software nas lojas de Aplicativos, como a Windows Store ou a App Store.

Para isso, adicione o código abaixo no seu package.json:

  "author": {
    "name": "Seu nome",
    "email": "seu.email@email.com",
    "url": "https://seu-site.com"
  },
  "homepage": "./",
  "main": "public/electron.js",
  "build": {
    "appId": "com.seu-site.seu-app",
    "productName": "MyApp",
    "copyright": "Copyright © 2019 Seu Nome",
    "mac": {
      "category": "public.app-category.utilities"
    },
    "files": [
      "build/**/*",
      "node_modules/**/*"
    ],
    "directories": {
      "buildResources": "assets"
    }
  },

Agora, precisamos alterar os scripts da nossa aplicação para que eles utilizem o rescripts em vez do react-scripts.

Além disso, criaremos também os scripts de build da nossa aplicação, para isso, os scripts do seu package.json devem ficar da seguinte maneira:

  "scripts": {
    "start": "rescripts start",
    "build": "rescripts build",
    "test": "rescripts test",
    "eject": "react-scripts eject",
    "dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\"",
    "postinstall": "electron-builder install-app-deps",
    "preelectron-build": "yarn build",
    "electron-build": "electron-builder -mwl"
  },

Nós criamos 3 scripts novos, são eles:

  • postinstall: Esse script irá instalar as dependências do electron builder sempre que rodarmos o comando install. Esse script é uma recomendação do electron-builder para que as dependências dele estejam sempre atualizadas.
  • electron-build: Esse script fará o build do nosso projeto. Nele, podemos passar algumas flags para informamos para quais plataformas o projeto deve ser buildado. Nesse caso, passamos a flag -mwl para informamos que o build deve ser para o MacOS (m), Windows (w) e Linux (l).
  • preelectron-build: Esse script irá buildar o nosso projeto CRA e gerar os arquivos estáticos. Como criamos esse script com o pre antes do electron-build, ele sempre irá executar antes do electron-build.

Com tudo isso configurado, rode o comando abaixo:

yarn electron-build

Quando rodarmos esse comando, o electron-builder irá gerar os arquivos executáveis do nosso projeto.

O processo de build pode demorar alguns minutos, principalmente se você está gerando o build para várias plataformas.

Os arquivos gerados no processo de build podem ser encontrados na pasta dist.

Ao finalizar e buildar sua aplicação, sua estrutura de pastas deve estar semelhante a essa:

Concluindo

Nesse post pudemos ver um pouco do poder do Electron. Aprendemos também como realizar o build de projetos criados com Electron e React Js.

Espero que esse post tenha te ajudado de alguma forma 🙂

Ver código no Github ↗

Forte Abraço,

Carlos Levir

Como criar notificações personalizadas no React Js.

Como criar notificações personalizadas no React Js.

A exibição de notificações é algo muito importante para que os usuários possam ter um feedback de uso da sua aplicação.

Hoje, vamos aprender como exibir notificações personalizadas, através da biblioteca react-toastify.

Passo #1: Criação do projeto

Para começamos, criaremos um projeto através do Create React App.

Você pode criar seu projeto através do comando abaixo:

create-react-app app

Com o processo finalizado, você pode entrar na pasta app que você criou e rodar o comando:

yarn start

Após alguns segundos, uma nova janela no seu navegador se abrirá, semelhante a essa:

Tela padrão de projetos criados através do CRA.

Passo #2: Instalação das bibliotecas

Agora, instalaremos as bibliotecas que usaremos no nosso projeto.

Para isso, dentro da pasta do seu projeto, rode o comando abaixo:

yarn add react-toastify styled-components

O react-toastify é a biblioteca que utilizaremos para exibir as notificações personalizadas.

O styled-components é a biblioteca que vai nos ajudar a estilizar nossa aplicação.


Passo #3: Configurações gerais

Em todos os projetos que eu crio utilizando Create React App, eu sempre gosto de fazer algumas configurações para deixar o projeto mais organizado.

Começaremos apagando alguns arquivos na pasta src, são eles:

  • App.css
  • App.test.js
  • index.css
  • logo.svg
  • serviceWorker.js

Apague também o arquivo manifest.json, que está na pasta public.

Precisamos também remover a importação desses arquivos.

O seu index.js deve ficar da seguinte forma:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

E o seu arquivo App.js, deve ficar da seguinte forma:

import React from 'react';

function App() {
  return <div />;
}

export default App;

Para finalizar a parte de configuração, criaremos um arquivo de estilos globais para nossa aplicação.

Para isso, crie uma pasta chamada styles, dentro da pasta src, e nela, crie um arquivo index.js.

Adicione a esse arquivo o seguinte código:

import styled, { createGlobalStyle } from 'styled-components';

export const Container = styled.div`
  display: flex;
  width: 100%;
  height: 100%;
`;

export default createGlobalStyle`
  * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      outline: 0;
    }
    html,
    body,
    #root {
      height: 100%;
    }
    body {
      text-rendering: optimizeLegibility !important;
      font-family: sans-serif;
      -webkit-font-smoothing: antialiased;
      background: #eee;
    }
`;

Essas configurações nos ajudarão a trabalhar melhor com tamanhos nos nossos componentes.

Nesse momento, sua estrutura deve estar semelhante a essa:

Estrutura de pastas após as configurações iniciais do projeto.

Passo #4: Configuração das notificações

Agora, faremos a configuração para que o react-toastify funcione corretamente.

Por padrão, o react-toastify não vem com estilos configurados, mas ele nos oferece um arquivo de estilos para utilizarmos.

No arquivo index.js da pasta styles, adicione a seguinte linha de código, logo antes do import do styled-components:

import 'react-toastify/dist/ReactToastify.css';

Agora, no arquivo App.js, adicionaremos os estilos que criamos no passo #3, e, o ToastContainer do react-toastify.

Seu App.js deve ficar da seguinte forma:

import React from 'react';
import { ToastContainer } from 'react-toastify';

import GlobalStyle from "./styles";

function App() {
  return (
    <>
      <GlobalStyle />
      <ToastContainer />
    </>
  );
}

export default App;

Passo #5: Criação da página de exemplo

Para esse tutorial, criaremos uma página de exemplo para testarmos a exibição das notificações.

Para isso, crie uma pasta pages na pasta src.

Dentro da pasta pages, crie uma pasta Main e nela, crie um arquivo index.js.

Adicione ao arquivo index.js da pasta Main, o seguinte conteúdo:

import React from 'react';
import { toast } from 'react-toastify';

import { Container, Buttons } from './styles';

function Main() {
  function handleDefault() {
    toast('Mensagem default');
  }
  function handleError() {
    toast.error('Mensagem error');
  }
  function handleSuccess() {
    toast.success('Mensagem success');
  }
  function handleInfo() {
    toast.info('Mensagem info');
  }
  function handleWarn() {
    toast.warn('Mensagem warn');
  }
  function handleCustom() {
    toast('Mensagem customizada', {
      position: toast.POSITION.TOP_LEFT,
      className: 'sua-classe',
    });
  }

  return (
    <Container>
      <Buttons>
        <button type="button" onClick={handleDefault}>
          Default
        </button>
        <button type="button" onClick={handleError}>
          Error
        </button>
        <button type="button" onClick={handleSuccess}>
          Success
        </button>
        <button type="button" onClick={handleWarn}>
          Warn
        </button>
        <button type="button" onClick={handleInfo}>
          Info
        </button>
        <button type="button" onClick={handleCustom}>
          Custom
        </button>
      </Buttons>
    </Container>
  );
}

export default Main;

Nesse arquivo, nós criamos alguns botões e funções para exibição das notificações.
O react-toastify nos oferece alguns tipos de notificações que podemos utilizar, são eles:

  • Default, container branco com um time colorido.
  • Error, container vermelho.
  • Success, container verde.
  • Warn, container amarelo.
  • Info, container azul.

Você também pode criar seu próprio estilo de notificações, alterando a posição do objeto ou passando uma classe diferente para editar o estilo das notificações, como foi mostrado na função handleCustom.

Agora, criaremos alguns estilos para nossa página, para isso, ainda na pasta Main, crie um arquivo styles.js, com o seguinte código:

import styled from 'styled-components';

export const Container = styled.div`
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
`;

export const Buttons = styled.div`
  display: flex;
  flex-wrap: wrap;
  width: 20%;

  & button {
    width: 100%;
    height: 40px;
    display: flex;
    justify-content: center;
    border-radius: 4px;
    font-weight: bold;
    font-size: 14px;
    border: 0;
    color: #fff;
    background: #009cde;
    margin-top: 5px;
    cursor: pointer;
  }
`;

Nesse arquivo, nós apenas criamos alguns estilos para os botões da nossa página de exemplo.

Passo #6: Finalizando a aplicação

Para finalizarmos a aplicação, precisamos importar a página de exemplo que criamos.

Para isso, deixe seu App.js da seguinte maneira:

import React from 'react';
import { ToastContainer } from 'react-toastify';

import Main from './pages/Main';

import GlobalStyle from './styles';

function App() {
  return (
    <>
      <GlobalStyle />
      <ToastContainer />
      <Main />
    </>
  );
}

export default App;

O GlobalStyle contém os estilos que criamos no passo #3.

Ao finalizar sua aplicação, sua estrutura deve estar semelhante à essa:

Demonstração

Confira como ficou o resultado final:

Ver código no Github ↗

Forte Abraço,

Carlos Levir

Como criar rotas autenticadas no React Js.

Como criar rotas autenticadas no React Js.

A criação de rotas autenticadas é muito importante quando falamos de rotas de uma aplicação.

Hoje, vamos aprender, de maneira simples, como criar rotas que precisem de autenticação na sua aplicação em React Js.

Passo #1: Criação do projeto

Para começamos, criaremos um projeto através do Create React App.

Você pode criar seu projeto através do comando abaixo:

create-react-app app

Com o processo finalizado, você pode entrar na pasta app que você criou e rodar o comando:

yarn start

Após alguns segundos, uma nova janela no seu navegador se abrirá, semelhante à essa:

Tela padrão de projetos criados através do CRA.

Passo #2: Instalação das bibliotecas

Agora, instalaremos as bibliotecas que usaremos no nosso projeto.

Para isso, dentro da pasta do seu projeto, rode o comando abaixo:

yarn add react-router-dom prop-types

O react-router-dom é a biblioteca responsável por toda a parte de navegação da nossa aplicação.

O prop-types é uma biblioteca oferecido pela própria equipe do React para trabalharmos com os tipos de props dos nossos componentes.


Passo #3: Configurações gerais

Em todos os projetos que eu crio utilizando Create React App, eu sempre gosto de fazer algumas configurações para deixar o projeto mais organizado.

Começaremos apagando alguns arquivos na pasta src, são eles:

  • App.css
  • App.test.js
  • index.css
  • logo.svg
  • serviceWorker.js

Apague também o arquivo manifest.json, que está na pasta public.

Precisamos também remover a importação desses arquivos.

O seu index.js deve ficar da seguinte forma:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

E o seu arquivo App.js, deve ficar da seguinte forma:

import React from 'react';

function App() {
  return <div />;
}

export default App;

Nesse momento, sua estrutura deve estar semelhante à essa:


Passo #4: Criação das páginas

Para essa aplicação, usaremos duas páginas para representar as rotas autenticadas e as não autenticadas.

Para começar, crie uma pasta pages dentro da pasta src.

Dentro da pasta pages, crie uma pasta Login.

Na pasta Login, crie um arquivo index.js

Adicione ao arquivo index.js da pasta Login, o seguinte conteúdo:

import React from 'react';

function Login({ history }) {
  function handleLogin() {
    localStorage.setItem('@SuaAplicacao:JWT_TOKEN', 'seutokenjwt');

    history.push('Main');
  }

  return (
    <form onSubmit={handleLogin}>
      <input type="text" name="email" placeholder="Seu email" required />
      <input type="password" name="password" placeholder="Sua senha" required />
      <button type="submit">Login</button>
    </form>
  );
}

export default Login;

Esse página representará a nossa tela de login.

Nela, a função handleLogin salva um token no localStorage. Esse token seria o token JWT que receberíamos da nossa API.

Em seguida, a função handleLogin faz o redirecionamento para a rota Main, que será uma rota autenticada.

Agora, criaremos a página Main.

Para isso, podemos repetir o processo de criação da pasta Login.

Crie uma pasta Main, e nela, crie um arquivo index.js

No index.js da pasta Main, adicionaremos o seguinte conteúdo:

import React from 'react';

function Main({ history }) {
  function handleLogout() {
    localStorage.removeItem('@SuaAplicacao:JWT_TOKEN');

    history.push('/');
  }

  return (
    <div>
      <h3>Bem vindo</h3>
      <button type="button" onClick={handleLogout}>
        Sair
      </button>
    </div>
  );
}

export default Main;

Nessa página, temos uma mensagem de boas vindas e um botão para sair.

O botão para sair chama a função handleLogout.

A função handleLogout remove o token que salvamos no login e, em seguida, nos redireciona novamente para a tela de Login.


Passo #5 Criação das rotas

As rotas serão definitivamente a parte mais importantes da nossa aplicação.

Para começarmos, crie uma pasta routes dentro da pasta src.

Dentro da pasta routes, crie um arquivo Route.js e adicione à ele o seguinte conteúdo:

import React from 'react';
import PropTypes from 'prop-types';
import { Redirect, Route } from 'react-router-dom';

function RouteWrapper({
  redirectTo, isPrivate, component: Component, ...rest
}) {
  const authenticated = localStorage.getItem('@SuaAplicacao:JWT_TOKEN');

  if (!authenticated && isPrivate) return <Redirect to={redirectTo} />;

  return <Route {...rest} render={props => <Component {...props} />} />;
}

RouteWrapper.propTypes = {
  redirectTo: PropTypes.string,
  isPrivate: PropTypes.bool,
  component: PropTypes.oneOfType([PropTypes.element, PropTypes.func])
    .isRequired,
};

RouteWrapper.defaultProps = {
  redirectTo: '/',
  isPrivate: false,
};

export default RouteWrapper;

Esse arquivo será um wrapper das nossas rotas.

Nesse wrapper, nós fazemos uma verificação se o usuário não está autenticado (através do token que definimos no login), e se a rota que ele está tentando acessar é um rota privada (através da prop isPrivate).

Caso duas condições sejam verdadeiras, nós o redirecionamos para a rota que recebemos através da prop redirectTo, que tem como valor padrão a rota raiz.

Caso a rota não seja privada ou o usuário esteja autenticado, nós o redirecionamos para a rota que ele está tentando acessar.

Utilizamos o oneOfType para que o tipo de componente possa ser tanto um Stateful, como um Stateless Component.

A partir de agora, utilizaremos ele como o nosso Route em vez do Route do react-router-dom.

Agora, criaremos um arquivo index.js na pasta routes, com o seguinte conteúdo:

import React from 'react';
import { Switch } from 'react-router-dom';

import Login from '../pages/Login';
import Main from '../pages/Main';

import Route from './Route';

export default function Routes() {
  return (
    <Switch>
      <Route path="/" exact component={Login} />
      <Route path="/main" exact component={Main} isPrivate />
    </Switch>
  );
}

Perceba que não importamos o Route do react-router-dom, mas sim do wrapper que criamos.

Para definirmos que uma rota é privada, basta que passemos a propriedade isPrivate, como fazemos com a rota /main.

Passo #7: Finalizando a aplicação

Para finalizar, precisamos importar todas as modificações que fizemos até agora.

Para isso, deixe seu App.js da seguinte maneira:

import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import Routes from './routes';

function App() {
  return (
    <BrowserRouter>
      <Routes />
    </BrowserRouter>
  );
}

export default App;

Ao utilizar o react-router-dom, é necessário que todas as nossas rotas fiquem dentro de um Router.

O Router mais utilizado para esse tipo de aplicação é o BrowserRouter.

Ao finalizar sua aplicação, sua estrutura deve estar semelhante à essa:


Demonstração

Abaixo você pode conferir uma demonstração do projeto:

Ver código no github ↗

Forte Abraço,

Carlos Levir

Como criar uma Single Page Application (SPA) com React Js.

Como criar uma Single Page Application (SPA) com React Js.

As Single Page Applications (SPA) vem se popularizando bastante nos últimos tempos.

Isso se dá devido seu grande foco em experiência do usuário.

Hoje, vamos aprender como construir uma SPA completa do zero, com o React Js.

Passo #1: Criação do projeto

Para esse projeto, utilizaremos o Create React App (CRA), que é a ferramenta de linha de comando oferecido pela própria equipe do React.

Caso você ainda não a tenha, você pode instala-la globalmente através do npm, com o seguinte comando:

npm i -g create-react-app

Com o CRA instalado, escolha uma pasta para salvar o seu projeto e dentro dessa pasta, rode o comando abaixo:

create-react-app spa

O CRA dará início a criação do seu projeto, esse processo pode demorar um pouco (é o tempo de ir pegar um café e voltar).

Com o processo finalizado, você pode entrar na pasta spa que você criou e rodar o comando:

yarn start

Após alguns segundos, uma nova janela no seu navegador se abrirá, semelhante à essa:

Tela padrão de projetos criados através do CRA.

Passo #2: Instalação das bibliotecas

Agora, instalaremos as bibliotecas que usaremos no nosso projeto.

Para isso, dentro da pasta do seu projeto, rode o comando abaixo:

yarn add react-router-dom styled-components

O react-router-dom é a biblioteca responsável por toda a parte de navegação da nossa SPA.

O styled-components é a biblioteca que vai nos ajudar a estilizar nossa aplicação.

Eu, particularmente, utilizo essas duas bibliotecas em praticamente todos os meus projetos.


Passo #3: Configurações gerais

Em todos os projetos que eu crio utilizando Create React App, eu sempre gosto de fazer algumas configurações para deixar o projeto mais organizado.

Começaremos apagando alguns arquivos na pasta src, são eles:

  • App.css
  • App.test.js
  • index.css
  • logo.svg
  • serviceWorker.js

Apague também o arquivo manifest.json, que está na pasta public.

Precisamos também remover a importação desses arquivos.

O seu index.js deve ficar da seguinte forma:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

E o seu arquivo App.js, deve ficar da seguinte forma:

import React from 'react';

function App() {
  return <div />;
}

export default App;

Para finalizar a parte de configuração, criaremos um arquivo de estilos globais para nossa aplicação.

Para isso, crie uma pasta chamada styles, dentro da pasta src, e nela, crie um arquivo index.js.

Adicione a esse arquivo o seguinte código.

import styled, { createGlobalStyle } from 'styled-components';

export const Container = styled.div`
  display: flex;
  width: 100%;
  height: 100%;
`;

export default createGlobalStyle`
  * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      outline: 0;
    }
    html,
    body,
    #root {
      height: 100%;
    }
    body {
      text-rendering: optimizeLegibility !important;
      font-family: sans-serif;
      -webkit-font-smoothing: antialiased;
      background: #eee;
    }
`;

Essas configurações nos ajudarão a trabalhar melhor com tamanhos nos nossos componentes.

Nesse momento, sua estrutura deve estar semelhante à essa:

Estrutura de pastas após as configurações iniciais do projeto.

Passo #4: Criação das páginas

Nesse momento, podemos dar início à construção das páginas da nossa aplicação.

Começaremos criando uma pasta chamada pages, dentro da pasta src.

Dentro da pasta pages, crie uma pasta chamada Main.

Na pasta Main, crie dois arquivos, o index.js e o styles.js. Essa será nossa primeira página.

Adicione ao arquivo index.js o seguinte conteúdo:

import React from 'react';

import { Container, Title } from './styles';

function Main() {
  return (
    <Container>
      <Title>Main</Title>
    </Container>
  );
}

export default Main;

Nessa página, nós utilizamos os estilos do nosso arquivo styles.js, que criaremos agora.

Para isso, adicione ao seu arquivo styles.js o seguinte código:

import styled from 'styled-components';

export const Container = styled.div`
  display: flex;
  width: 100%;
  justify-content: center;
  align-items: center;
`;

export const Title = styled.h1`
  color: #009cde;
`;

Criaremos agora a segunda página da nossa aplicação.

Nós podemos duplicar a pasta Main que criamos e alterar seu nome para Contact.

O arquivo index.js da pasta Contact deve ficar da seguinte forma:

import React from 'react';

import { Container, Title } from './styles';

function Contact() {
  return (
    <Container>
      <Title>Contato</Title>
    </Container>
  );
}

export default Contact;

Podemos manter o arquivo de estilos como está.


Passo #5: Criação da Sidebar

A navegação com um Sidebar é o tipo mais comum em SPAs, pois elas facilitam muito a navegação do usuário.

Começaremos criando uma pasta components, dentro da pasta src.

Na pasta components, crie uma pasta Sidebar, e nela, crie dois arquivos, novamente o index.js e o styles.js.

Ao arquivo styles.js, adicione o seguinte conteúdo:

import styled from 'styled-components';
import { NavLink } from 'react-router-dom';

export const Container = styled.aside`
  display: flex;
  flex-shrink: 0;
  align-items: center;
  width: 100px;
  height: 100%;
  background-image: linear-gradient(to bottom right, #009cde, #0073cc);
  flex-direction: column;

  transition: all 0.2s ease 0s;

  &:hover {
    width: 240px;
  }
`;

export const SidebarLink = styled(NavLink)`
  width: 100%;
  text-align: center;
  text-decoration: none;
  padding-top: 10px;
  color: #eee;

  &:hover {
    color: #fff;
  }
`;

Nesse arquivo de estilos, nós importamos o NavLink que o react-router-dom nos oferece, e, adicionamos alguns estilos à ele, em vez de simplesmente importar ele no nosso index.js.

O arquivo index.js deve ficar com o seguinte conteúdo:

import React from 'react';

import { Container, SidebarLink } from './styles';

function Sidebar() {
  return (
    <Container>
      <SidebarLink to="/">Início</SidebarLink>
      <SidebarLink to="/contact">Contato</SidebarLink>
    </Container>
  );
}

export default Sidebar;

Nesse arquivo, nós importamos os 2 componentes que criamos no nosso arquivo de estilos, o Container e o SidebarLink, que é o NavLink importado do react-router-dom.

O NavLink recebe um atributo to que deve ser a rota que o usuário será redirecionado quando clicar naquele link.


Passo #6: Criação das rotas

As rotas são definitivamente uma das partes mais importantes de uma Single Page Application.

Para configurar as rotas, começaremos criando uma pasta chamada routes.

Dentro da pasta routes, criaremos um novo arquivo index.js.

É uma boa prática separarmos nossas rotas em arquivos diferentes, pois isso facilita a organização conforme nossa aplicação vai crescendo e aumentando o número de rotas.

Agora, adicionaremos o seguinte código ao arquivo index.js das nossas rotas:

import React from 'react';
import { Switch, Route } from 'react-router-dom';

import Main from '../pages/Main';
import Contact from '../pages/Contact';

export default function Routes() {
  return (
    <Switch>
      <Route path="/" exact component={Main} />
      <Route path="/contact" exact component={Contact} />
    </Switch>
  );
}

Nesse código, nós importamos o Switch e o Route do react-router-dom.

O Switch atuará de forma semelhante ao switch/case presente no Javascript, ele verifica qual rota atende os parâmetros necessário e renderiza a primeira rota compatível.

O Route pode receber, o primeiro deles é o path, que será o caminho acessado pelo usuário no navegador.

Caso o usuário acesse www.carloslevir.com/contato, o path que ele estará acessando será contato.

Caso o usuário acesse a rota raiz de www.carloslevir.com, por exemplo, ele estará acessando o path /.

A opção exact é muito importante, ela serve para informar ao Switch que a rota acessada pelo usuário deve ser exatamente igual a rota que informamos.

Caso não informemos a opção exact, quando o usuário acessar uma rota que simples comece com a /, como o /app por exemplo, o Switch traria a rota raiz, já que a rota /app satisfaz a condição da rota raiz (/).


Passo #7: Finalizando a aplicação

Agora, precisamos importar todas as modificações que fizemos até agora.

Para isso, deixe seu App.js da seguinte maneira:

import React from 'react';
import { BrowserRouter } from 'react-router-dom';

import Sidebar from './components/Sidebar';
import Routes from './routes';

import GlobalStyle, { Container } from './styles';

function App() {
  return (
    <BrowserRouter>
      <GlobalStyle />
      <Container>
        <Sidebar />
        <Routes />
      </Container>
    </BrowserRouter>
  );
}

export default App;

Ao utilizar o react-router-dom, é necessário que todas as nossas rotas fiquem dentro de um Router.

O Router mais utilizado para esse tipo de aplicação é o BrowserRouter.

Nós utilizamos o BrowserRouter no App.js em vez de usa-lo no próprio arquivo de rotas, pra que os componentes fora das rotas possam utiliza-los, como o Sidebar.

O Container e o GlobalStyle são os estilos que criamos no começo do projeto.

Ao finalizar sua aplicação, sua estrutura deve estar semelhante à essa:

Estrutura de pastas após a finalização do projeto.

Demonstração

Abaixo você pode conferir uma demonstração do projeto:

Ver código no github ↗

Forte Abraço,

Carlos Levir