Olá amigos, hoje vamos construir um componente simples em ReactJS, mas passaremos por vários conceitos importantes.

GIF do componente com estado e com input

Motivação

Componentes nos permite dividir nossa interface em partes independentes e reusáveis e isso nos ajuda a pensar cada parte de forma isolada.

Por exemplo, quando estamos construindo um site ou um sistema, os elementos tendem a se repetir para assim mantermos uma identidade visual coerente. Os botões tendem a ter as mesmas características de fonte, borda, raio da borda, cor de fundo, …

Como os elementos se repetem, podemos componentizar e depois usá-los quantas vezes quisermos. Outro benefício é que se um dia precisarmos mudar a cor de todos os botões de azul para vermelho, faremos isso em apenas 1 arquivo e não será preciso procurar no HTML todos os botões e ir trocando a cor.

Dessa forma tais princípios são atendidos:

  1. Reusabilidade
    1. Escrevemos o código apenas uma vez e reusamos quantas vezes for necessário
  2. Separação de conceitos (Separation of concerns)
    1. Um princípio que visa modularizar o código de forma que cada parte seja responsável por apenas uma função. Com isso, o código fica mais organizado e mais fácil de manter.

Vamos começar

Iniciaremos criando um React app usando o boilerplate fornecido pela equipe de desenvolimento do ReactJS: link.

Execute o seguinte comando no terminal:

npx create-react-app hello-react

Ele irá criar e um configurar um projeto inicial em React com tudo o que a gente precisa para começar. E não se engane, de iniciantes a seniors, quase todos desenvolvedores usam esse boilerplate.

Após criado o projeto, abra-o em seu editor preferido, no meu caso, vou usar o VS Code.

Seu Primeiro Componente

Digamos que queremos criar um componente que informe o nível de aprovação de um filme. O nosso componente tem uma aparencia similar a de uma barra de loading e pode apresentar valores de 0 a 100. O preenchimento da barra deve ser condizente com o nível de aprovação.

Estrutura de pastas

No editor de texto criaremos nosso componente em src/components/approval/aproval.jsx. Algumas dessas pastas não existe e você deve criá-las. O ReactJS não te obriga a seguir uma estrutura de pasta, você pode organizar seu projeto do jeito que desejar, porém tendemos a seguir alguns padrões para um boa organização do código. Seu projeto deve ficar mais ou menos assim:

- public
- src
   - App.css
   - App.js
   ...
   - components
      - approval
         - approval.jsx

Componente

Iremos utilizar a abordagem funcional para criação do componente, falo isso porque por muito tempo os componentes que guardavam estado no React precisa ser construido utilzando Classes, conforme o React tem avançado, cada vez mais utilizamos a abordagem funcional e uma tendencia que observamos é que componentes construído utilizando classes vão ser depreciados no futuro. No nosso arquivo approval.jsx, precisamos importar o React e criar uma função. Ele inicialmente deverá ficar assim:

// components/approval/approval.jsx

import React from 'react';

const Approval = props => {

}

export default Approval;

Essa a estrutura básica de um componente no React, você escreverá esse código diversas vezes, sugiro até criar um snippet no seu editor de texto para ganhar produtividade. A única alteração que você precisará executar será o nome da função. Agora iremos estilizar um pouco o nosso componente, para isso, utilizaremos o CSS dentro do arquivo JS (Sugiro a leitura da documentação)

Agora iniciaremos com uma estilização básica, utilizando o CSS dentro do ReactJS. Se você ainda não sabe como fazer isso, você pode ler a documentação ou ver esse post no Medium.

// components/approval/approval.jsx

import React from 'react';

const Approval = props => {

    const approvalStyles = {
        margin: '10px',
        width: '200px',
        height: '25px',
        display: 'block',
        border: '1px solid #ccc',
        borderRadius: '7px',
    }

    const ratingStyles = {
        width: '50px',
        height: '25px',
        borderRadius: '7px',
        display: 'block',
        backgroundColor: '#ccc',
    }

    return (
    <div className='approval' style={approvalStyles}>
        <div className='rating' style={ratingStyles}></div>
    </div>
    )
}

export default Approval;

Em HTML o que você vê no snippet acima seria o equivalente a:

<html>
    <head>
      <style>
        div.approval {
          margin: 10px;
          width: 200px;
          height: 25px;
          display: block;
          border: 1px solid #ccc;
          border-radius: 7px;
        }

        div.rating {
          width: 50px;
          height: 25px;
          border-radius: 7px;
          display: block;
          background-color: #ccc;
        }
      </style>
    </head>

    <body>
      
      <div class='approval'>
        <div class='rating'>
          ...
        </div>
      </div>

    </body>
</html>

Continuando…

Todo componente no React deve retornar alguma coisa. Esse coisa chamamos de JSX e o React irá renderizar o elemento para gente. Dois pontos eu gostaria de destacar no código acima.

  1. Como class é uma palavra reservada no JS, precisamos chamar as classes do HTML de className.
  2. Os estilos dentro de um arquivo JS é um objeto e as chaves do objeto devem estar no padrão camelCase, assim, o seletor border-radius vira borderRadius, esse padrão se repete para qualquer seletor do CSS.

Agora iremos para o arquivo src/App.js, apagaremos quase tudo que tem lá e importaremos nosso primeiro componente. Ele deverá ficar assim:

// App.jsx

import Approval from './components/approval/approval'

function App() {
  return (
    <div className=""App"">
      <Approval />
    </div>
  );
}

export default App;

E agora podemos iniciar o server para visualizar como ficou. Rode o seguinte comando no terminal e acesse localhost:3000

npm start

Você deverá ver isso:

Primeiro componente

Adicionando estado ao componente

Para adicionar estado ao componente, usaremos React Hooks, uma funcionalidade nova bastante poderosa e simples de usar.

Em tempos passados, os estados dos componentes eram administrados de forma diferente e só era possível criar componentes que guardavam estado utilizando classes.

Um componente sem estado e que irá ser renderizado de acordo com os parâmetros é denominado de componente puro (pure component) (Veja mais aqui). Em uma aplicação React temos uma predominância de componentes puros, pois assim se tornar mais fácil a manutenção do código, pois todo o estado da aplicação está centralizado em um (ou poucos) componentes.

O Estado no nosso componente irá representar o nível de aprovação. O nível zero será representado pela barra vazia e o nível cem será representado pela barra cheia. Para utlizar os Hooks precisamos primeiro importá-los no nosso componente.

// components/approval/approval.jsx

import React, { useState } from 'react';

Após importado, precisaremos criar uma nova variável e uma função para alterar seu valor. Para isso, utilizaremos o conceito de desestruturação do Javascript.

// components/approval/approval.jsx

const [approvalRating, setApprovalRating] = useState(45);

O useState retorna um array e utilizamos a desestruração para obter o primeiro e o segundo elemento desse array. O primeiro elemento é a variável e o segundo elemento a função responsável por atualizar o valor na variável e em seguida atualizar o componente na tela do usuário. O valor entre parenteses useState(45) é o valor inicial da variável.

Agora para que o componente seja preenchido conforme o valor da variável, precisaremos alterar um pouco o objeto ratingStyles

// components/approval/approval.jsx

const ratingStyles = {
  width: `${(200 * approvalRating) / 100}px`,
  height: '25px',
  borderRadius: '7px',
  display: 'block',
  backgroundColor: '#ccc',
}

Feito isso, o componente será preenchido conforme o valor de approvalRating. Agora podemos ficar alterando o valor inicial de approvalRating e vermos o tamanho da barra sendo modificada.

Componente com estado

Adicionando input para alteração do estado

Mas seria muito ruim ter que ficar mudando no código o valor do estado do componente. Para melhorar nosso componente, iremos adicionar um campo de texto para que o usuário possa alterar o valor da forma que ele desejar.

Adicionaremos um input e seu estilo. O componente deverá ficar assim:

// components/approval/approval.jsx

const Approval = (props) => {
  const [approvalRating, setApprovalRating] = useState(10);

  const approvalStyles = {
    margin: "10px",
    width: "200px",
    height: "25px",
    display: "block",
    border: "1px solid #ccc",
    borderRadius: "7px",
  };

  const ratingStyles = {
    width: `${(200 * approvalRating) / 100}px`,
    height: "25px",
    borderRadius: "7px",
    display: "block",
    backgroundColor: "#ccc",
    transition: ".2s",
  };

  const inputStyles = {
    marginTop: "16px",
    width: "100%",
    height: "25px",
    boxSizing: "border-box",
    paddingLeft: "8px",
  };

  return (
    <div className="approval" style={approvalStyles}>
      <div className="rating" style={ratingStyles}></div>
      <input type="number" max="100" style={inputStyles} />
    </div>
  );
}

Agora iremos fazer duas coisas:

  1. Adicionar o valor de approvalRating no campo
  2. Adicionar uma função de alteração do approvalRating

Assim, o input deverá ficar assim:

// components/approval/approval.jsx

<input
  type="number"
  max="100"
  style={inputStyles}
  value={approvalRating}
  onChange={(e) => setApprovalRating(e.target.value)}
/>

E agora ao alterar o valor do input, o tamanho da barra do componente também irá mudar. Como sabemos que o valor não pode passar de 100, podemos incrementar ainda um pouco mais a função de alteração para fazer uma validação e não permitir valores maiores que 100.

A função e o componente deverão ficar assim:

// components/approval/approval.jsx

const onChangeApprovalHandler = (e) => {
  if (e.target.value <= 100) {
    setApprovalRating(e.target.value);
  }
};

return (
  <div className="approval" style={approvalStyles}>
    <div className="rating" style={ratingStyles}></div>
    <input
      type="number"
      max="100"
      style={inputStyles}
      value={approvalRating}
      onChange={onChangeApprovalHandler}
    />
  </div>
);

Componente com estado e com input

Para finalizar, perceba que a transição do comprimento da barra ao ser alterado o valor do campo é muito bruta. Iremos deixar a transição mais suave adicionando apenas uma linha de CSS no objeto ratingStyles

const ratingStyles = {
  width: `${(200 * approvalRating) / 100}px`,
  height: "25px",
  borderRadius: "7px",
  display: "block",
  backgroundColor: "#ccc",
  transition: ".3s",
};

Agora sim, temos uma transição mais fluida e elegante para o componente.

GIF do componente com estado e com input

Código completo

  • App.jsx
import logo from './logo.svg';
import './App.css';
import Approval from './components/approval/Approval';

function App() {
  return (
    <div className="App">
      <Approval/>
    </div>
  );
}

export default App;
  • Approval.jsx
import React, { useState } from "react";

const Approval = (props) => {
  const [approvalRating, setApprovalRating] = useState(10);

  const approvalStyles = {
    margin: "10px",
    width: "200px",
    height: "25px",
    display: "block",
    border: "1px solid #ccc",
    borderRadius: "7px",
  };

  const ratingStyles = {
    width: `${(200 * approvalRating) / 100}px`,
    height: "25px",
    borderRadius: "7px",
    display: "block",
    backgroundColor: "#ccc",
    transition: ".3s",
  };

  const inputStyles = {
    marginTop: "16px",
    width: "100%",
    height: "25px",
    boxSizing: "border-box",
    paddingLeft: "8px",
  };

  const onChangeApprovalHandler = (e) => {
    if (e.target.value <= 100) {
      setApprovalRating(e.target.value);
    }
  };

  return (
    <div className="approval" style={approvalStyles}>
      <div className="rating" style={ratingStyles}></div>
      <input
        type="number"
        max="100"
        style={inputStyles}
        value={approvalRating}
        onChange={onChangeApprovalHandler}
      />
    </div>
  );
};

export default Approval;

#Fim