React - Bora estilizar?
Published: 2021-02-19 12:58:18

##:wave: Introdução

Se você já esteve em algum projeto web com certeza já trabalhou com [CSS](https://developer.mozilla.org/pt-BR/docs/Web/CSS) ou algum dos seus pré-processadores (Sass, LESS, Stylus).
E no React não é diferente. Todas essas tecnologias também podem e devem ser utilizadas, porém vamos começar com o básico e ver como é simples adicionar CSS a um componente.

Primeiro vamos estilizar o `Header` da nossa aplicação, mas para isso teremos que modificar um pouco nosso arquivo. Precisamos criar uma passar com nome **Header**, assim como o arquivo `Header.tsx`. E dentro dessa pasta iremos criar um arquivo `index.tsx` copiando todo conteúdo que temos no `Header.tsx` sem mudar nada. Além disso iremos criar ainda dentro dessa pasta um arquivo `styles.css` para podermos importar dentro do componente.
Por fim apagamos o `Header.tsx`.

```css
/* components/Header/styles.css */

#header {
  background: #5636d3;
}
```

```react
// components/Header/index.tsx

import "./styles.css"

function Header() {
  return (
    <div>
      <header id="header">
        <span>gofinances</span>
        <nav>
          <a href="/">Listagem</a>
          <a href="/create">Cadastro</a>
        </nav>
      </header>
    </div>
  );
}

export default Header
```

Se o seu servidor ainda estiver rodando (se não, `npm start`) basta entrar no http://localhost:3000 e você verá que a cor de fundo do nosso componente **Header** está roxo. Simples assim para utilizar CSS no React. 

Caso você vá utilizar algum pré-processador, [Google](https://www.google.com.br/) é seu melhor amigo, mas já posso te adiantar que não deve ser nenhum monstro de sete cabeças nem nada do tipo, porque no fim é apenas importar o arquivo CSS para dentro do componente React e ele funcionará.

Mas essa abordagem de criar um arquivo CSS para cada componente tem o seu problema.

##:interrobang: Qual o problema?

Ao separarmos os arquivos CSS em cada componente já é algo muito bom, bem melhor do que ter um arquivo único com toda estilização da nossa aplicação. Porém, ai no seu código faça um teste:

- Duplique a pasta do componente **Header** e renomeio para um nome qualquer como **Header2**.
- Importe esse componente no `App.tsx` e adicione-o abaixo `<Header />`.
- No arquivo `styles.css` desse novo componente altere o `background` para uma cor qualquer (pode ser #000 que é preto).

Se você acompanhou os últimos posts dessa série isso deve ser fácil para você. 

Ao entrar no nosso projeto rodando no browser veremos que os dois **Headers** tem a mesma cor de fundo, a cor que você alterou. Mas porque isso aconteceu se criamos o CSS para cada componente?

Isso acontece porque estamos utilizando o mesmo [seletor CSS](https://developer.mozilla.org/pt-BR/docs/Web/CSS/Getting_Started/Seletores) para estilizar: `#header` e o React importa os dois arquivos dentro da página, assim o CSS que é carregado por último é o que sobrescreve o outro. Uma solução para isso seria utilizar seletores diferentes, como outro *id* para o segundo header ou uma classe diferente. 
Porém essa abordagem fica bem problemática quando o projeto cresce em escala, já que você não vai lembrar dos nomes que já foram dados anteriormente muito menos saber qual nome outro pessoa que trabalha no projeto utilizou.

E foi pensando nesse caso que vamos utilizar o [Styled Components](https://styled-components.com/).

##:triangular_ruler: Componentes Estilizados

O que Styled Components faz é: ele cria uma classe com identificador único para cada Componente Estilizado que criamos. Assim ele garante que ao criar um nova estilização não vamos estar afetando outro lugar da aplicação. Ele nada mais é do que um criador de CSS escrito em Javascript.
Se só isso não bastasse com ele também podemos criar temas para nossa aplicação de maneira simples e fácil, além de poder criar animações reutilizáveis entre vários componentes. Mas vamos fazer o simples primeiro.

Vamos instalar o pacote no nosso projeto:

```bash
npm i styled-components
```

Como esse pacote não é escrito em Typescript, temos que instalar também as tipagens. O `-D` serve para adicionarmos essa dependência apenas para o ambiente de desenvolvimento, já que em produção não estaremos utilizando Typescript.

```bash
npm i @types/styled-components -D
```

:pin: Se você estiver utilizando o VSCode, existe uma extensão que nos ajudará ao trabalhar com o styled-components que é [essa](https://marketplace.visualstudio.com/items?itemName=jpoissonnier.vscode-styled-components). Caso não seja o seu caso, na [documentação](https://styled-components.com/docs/tooling#syntax-highlighting) há ferramentas para outros editores de texto.

Agora, vamos alterar o arquivo `styles.css` para `styles.ts` e alterar seu conteúdo para: 

```typescript
// components/Header/styles.ts

import styled from 'styled-components'

export const Container = styled.div`
  background: #5636d3;
`
```

E o que é cada parte desse código?
- `export const Container` estamos exportando desse arquivo uma constante que por ser um componente estilizado deve sempre iniciar com letra maiúscula.
- `styled` é o nome que damos para a lib, poderia ser `batata` se quisermos, mas vamos seguir como mostra a documentação.
- `.div` indica qual será o retorno desse componente, podendo ser qualquer tag do HTML5.
- O conteúdo dentro das crases são o CSS em si, que será aplicado a essa `div` utilizando uma classe única.

:warning: Caso essa sintaxe pareça diferente para você tem um [artigo](https://www.alura.com.br/artigos/tagged-template-literals) bem legal da Alura que explica como funciona as Tagged Template Literals.

Para vermos o efeito dessa estilização no código temos que alterar o `Header/index.tsx`:

```react
// components/Header/index.tsx

import { Container } from "./styles";

function Header() {
  return (
    <Container>
      <header>
        <span>gofinances</span>
        <nav>
          <a href="/">Listagem</a>
          <a href="/create">Cadastro</a>
        </nav>
      </header>
    </Container>
  );
}

export default Header
```
Vemos que o `Container` é como qualquer outro componente, porém nele contém apenas estilização e nada mais.

A partir de agora temos duas maneiras de prosseguir, sendo elas:
- Podemos criar vários componentes estilizados dentro de um componente como o **Header**. Por exemplo, além do *Container*, outros como **Nav**, **Title** e **Link** cada um estilizando uma parte do nosso componente.
- Ou podemos utilizar o encadeamento das tags e ir estilizando tudo pelo componente container. Por exemplo, se quisermos que o `<span>` tenha uma cor de texto branca poderíamos fazer assim:

```typescript
// components/Header/styles.ts

import styled from 'styled-components'

export const Container = styled.div`
  background: #5636d3;

  span {
    color: #FFF
  }
`
```

Vamos seguir o segundo modelo, por praticidade.
 
##:recycle: Resetando os estilos globalmente

Muitos das tags do HTML tem uma própria estilização que pode nos atrapalhar. Podemos ver isso no nosso projeto:

![Print #1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3epkr7fn3xenq214a1ok.png)

A tag `body` tem uma 'margin' para todos os lados de 8px. E para resetar todos essas estilizações vamos criar um pasta `styles` dentro do `src`. Dentro dessa pasta, vamos criar um arquivo `global.ts` para nossa estilização global.

```react
//styles/global.ts

import { createGlobalStyle } from 'styled-components'

export default createGlobalStyle`
  * {
    margin: 0;
    padding: 0;
    outline: 0;
    box-sizing: border-box;
  }

  body {
    background: #F0F2F5 ;
    -webkit-font-smoothing: antialiased
  }

  body, input, button {
    font: 16px "Poppins", sans-serif;
  }

  button {
    cursor: pointer;
  }
`
```
Explicando o que esse arquivo faz:

- Primeiro importamos de dentro do 'styled-components' uma função que cria um estilização global (createGlobalStyle); e assim como fazíamos com uma `div`, escrevemos nosso código CSS entre crases.

- No CSS, estamos zerando todos os espaçamentos, mudando a cor de fundo padrão e até mudando a fonte da aplicação. Vale ressaltar a importância de ao alterar a fonte, mudar também na tag input e button, já que em ambos não são aplicados a fonte do body.

- Por fim, mudamos o cursor padrão de um botão para a mãozinha do click 👆.

Para importamos a fonte 'Poppins' no nosso projeto vamos utilizar o [Google Fonts](https://fonts.google.com/specimen/Poppins). Como esse processo pode mudar quando você estiver lendo isso vou apenas deixar meu código atual:

```html
<!-- public/index.html -->

<!DOCTYPE html>
<html lang="pt_BR">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap" rel="stylesheet">  
    <title>Go Finances</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
```

Agora temos que importar `styles/global.ts` no nosso `App.tsx` para que ele tenha efeito.

Mas antes, só para facilitar mais pra frente vamos passar parte do nosso código que está no `App.tsx` para outro componente.

> Mas porque temos que criar mais um componente? Não basta deixar tudo dentro do 'App'?

Lembrando uma das vantagens da componentização é a reutilização de código, e já que teremos mais uma página e em todas queremos que o `GlobalStyle` seja aplicado temos que separar a nossa página principal. E mais uma vez isso é bem simples.

Basta lembrar, sempre que criamos um compomente que terá estilização criamos uma pasta com o nome do componente e dentro criamos um arquivo `index.tsx` e outro `styles.ts`,

Criando uma pasta `pages` dentro de `src`, e criando um componente `Home` que contém:

```react
//pages/Home/index.tsx

import Header from "../../components/Header";
import TransactionList from "../../components/TransactionList";

function Home() {
  return (
    <>
      <Header />
      <TransactionList />
    </>
  );
}

export default Home;
```

E `App.tsx`:

```react
import Home from "./pages/Home";
import GlobalStyle from "./styles/global";

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

export default App;
```

##:lipstick: Deixando essa página um pouco mais bonita

Estilização é algo que é muito difícil de aprender em tutorial. É algo que se aprende de tanto fazer e copiar grandes designs. Se você trabalha com web já deve entender pelo menos um pouco sobre [Flexbox](https://developer.mozilla.org/pt-BR/docs/Web/CSS/CSS_Flexible_Box_Layout/Conceitos_Basicos_do_Flexbox) e/ou [Grid Layout](https://developer.mozilla.org/pt-BR/docs/Web/CSS/CSS_Grid_Layout), mas se esse não é o seu caso aconselho a buscar entender um pouco sobre esses modelos de layout depois e por enquanto só digitar o que eu vou mostrar aqui de estilização já que o foco desses posts é mostrar o React e não CSS.

Para você que já entende muito bem de CSS, aqui vamos utilizar o básico e praticamente nada de avançado como [Media Queries](https://developer.mozilla.org/pt-BR/docs/Web/Guide/CSS/CSS_Media_queries) e responsividade, vamos ser o mais básico possível.

Caso não queira copiar todo o código daqui pra frente, basta clonar o projeto apenas com a parte da estilização pronta:

```bash
git clone -b entrypoint https://github.com/brunolipe-a/go-finances.git
```
Mas eu aconselho a você digitar todo o código para fixar.

A estilização do **Header** fica assim:

```typescript
//components/Header/styles.ts

import styled from 'styled-components'

export const Container = styled.div`
  background: #5636d3;
  padding: 30px 0;

  header {
    width: 1120px;
    margin: 0 auto;
    padding: 0 20px 150px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    color: #fff;

    span {
      font-size: 1.75rem;
      font-weight: medium;
    }

    nav {
      a {
        color: #fff;
        text-decoration: none;
        font-size: 16px;
        transition: opacity 0.2s;
        padding-bottom: 6px;
        border-bottom: 2px solid #FF872C;

        & + a {
          margin-left: 32px;
          margin-right: 5px;
        }

        &:hover {
          opacity: 0.6;
        }
      }
    }
  }
`
```

Crie um arquivo em `src/utils/formatValue.ts`:
```typescript
const formatValue = (value: number): string =>
  Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(
    value,
  );

export default formatValue;
```

Novo componente *Cards*:
```typescript
//components/Cards/styles.ts

import styled from 'styled-components';

interface CardProps {
  total?: boolean;
}

export const CardContainer = styled.section`
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 32px;
  margin-top: -150px;
`;

export const Card = styled.div`
  background: ${({ total }: CardProps): string => (total ? '#FF872C' : '#fff')};
  padding: 22px 32px;
  border-radius: 5px;
  color: ${({ total }: CardProps): string => (total ? '#fff' : '#363F5F')};

  header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-weight: 500;
    a {
      background: #7159c1;
    }
    p {
      font-size: 16px;
    }
  }
  h1 {
    margin-top: 14px;
    font-size: 36px;
    font-weight: 500;
    line-height: 54px;
  }
`;
```
```react
//components/Cards/index.tsx

import formatValue from "../../utils/formatValue";
import { CardContainer, Card } from "./styles";

export interface Balance {
  income: number;
  outcome: number;
  total: number;
}

type CardsProps = {
  balance: Balance;
};

function Cards({ balance }: CardsProps) {
  return (
    <CardContainer>
      <Card>
        <header>
          <p>Entradas</p>
        </header>
        <h1>{formatValue(balance.income)}</h1>
      </Card>
      <Card>
        <header>
          <p>Saídas</p>
        </header>
        <h1>{formatValue(balance.outcome)}</h1>
      </Card>
      <Card total>
        <header>
          <p>Total</p>
        </header>
        <h1>{formatValue(balance.total)}</h1>
      </Card>
    </CardContainer>
  );
}

export default Cards;
```

Temos que remover o antigo componente *TransactionList* e criar novamente utilizando uma pasta:

```typescript
// components/TransactionList/styles.ts

import styled from 'styled-components';

export const TableContainer = styled.section`
  margin-top: 64px;
  table {
    width: 100%;
    border-spacing: 0 8px;
    th {
      color: #969cb3;
      font-weight: normal;
      padding: 20px 32px;
      text-align: left;
      font-size: 16px;
      line-height: 24px;
    }
    td {
      padding: 20px 32px;
      border: 0;
      background: #fff;
      font-size: 16px;
      font-weight: normal;
      color: #969cb3;
      &.title {
        color: #363f5f;
      }
      &.income {
        color: #12a454;
      }
      &.outcome {
        color: #e83f5b;
      }
    }
    td:first-child {
      border-radius: 8px 0 0 8px;
    }
    td:last-child {
      border-radius: 0 8px 8px 0;
    }
  }
`;
```

```react
// components/TransactionList/index.tsx

import formatValue from "../../utils/formatValue";

import { TableContainer } from "./styles";

export interface Transaction {
  id: string;
  title: string;
  value: number;
  type: "income" | "outcome";
  category: { title: string };
  created_at: string;
}

type TransactionListProps = {
  transactions: Transaction[];
};

function TransactionList({ transactions }: TransactionListProps) {
  return (
    <TableContainer>
      <table>
        <thead>
          <tr>
            <th>Título</th>
            <th>Preço</th>
            <th>Categoria</th>
            <th>Data</th>
          </tr>
        </thead>

        <tbody>
          {transactions.map((transaction) => (
              <tr key={transaction.id}>
                <td className="title">{transaction.title}</td>
                <td className={transaction.type}>
                  {formatValue(transaction.value)}
                </td>
                <td>{transaction.category?.title}</td>
                <td>
                  {new Date(transaction.created_at).toLocaleDateString("pt-br")}
                </td>
              </tr>
            ))}
        </tbody>
      </table>
    </TableContainer>
  );
}

export default TransactionList;
```
E no `src/pages/Home`:

```typescript
// pages/Home/styles.ts

import styled from 'styled-components';

export const Container = styled.div`
  width: 100%;
  max-width: 1120px;
  margin: 0 auto;
  padding: 40px 20px;
`;
```

```react
// pages/Home/index.tsx

import { useState } from "react";

import Header from "../../components/Header";

import Cards, { Balance } from "../../components/Cards";
import TransactionList, { Transaction } from "../../components/TransactionList";

import { Container } from "./styles";

function Home() {
  const [balance, setBalance] = useState<Balance>({
    income: 12.12,
    outcome: 6,
    total: 6.12,
  });

  const [transactions, setTransactions] = useState<Transaction[]>([
    {
      id: "1",
      category: { title: "Trabalho" },
      title: "Venda",
      type: "income",
      value: 12.12,
      created_at: "2021-02-03T00:00:00",
    },
    {
      id: "2",
      category: { title: "Comida" },
      title: "Lanche",
      type: "outcome",
      value: 6,
      created_at: "2021-02-21T00:00:00",
    },
  ]);

  return (
    <>
      <Header />
      <Container>
        <Cards balance={balance} />
        <TransactionList transactions={transactions} />
      </Container>
    </>
  );
}

export default Home;
```

Na **Home** temos algo novo: `useState<Transaction[]>`.
Utilizamos `<>` para informar para o `useState` qual a tipagem do valor a ser armazenado. Isso ajuda ao desenvolver pois o próprio VSCode vai informar se tentarmos salvar um valor que poderá quebrar código. 
Por exemplo, se fossemos salvar uma string seria: `useState<string>`. Vale lembrar que o valor inicial do estado deve ser do mesmo tipo da tipagem.

##:smile: Ficou bonito?

Aqui terminamos a estilização da tela de listagem. Aconselho a digitarem todo o código para guardar na mente como criar componentes estilizados. 

No próximo post vamos criar uma API de teste utilizando apenas JSON e começar a consumir essa API para gerar nossa tela dinamicamente.

PS: Um agradecimento especial:purple_heart: ao pessoal da [Rocketseat](https://rocketseat.com.br/):rocket: pelo conteúdo de qualidade em React.