Primeiro projeto: MiniPokedex
Para entender alguns conceitos primordiais do React, vamos começar com um projetinho simples.
Criando o projeto
Podemos usar o comando npx create-react-app@5 mini-pokedex para criar um novo projeto com o framework. Ao rodar esse comando vários arquivos deverão ser baixados então aguarde um pouco. Após isso, abra o projeto no VsCode.
Após isso, entre na pasta do projeto com o comando cd mini-pokedex e rode o comando npm start para iniciar o servidor de desenvolvimento. O projeto será aberto no navegador padrão.
Nesse ponto você tem um exemplo de projeto simples com React funcionando com vários arquivos como arquivos de teste, arquivos de imagem etc. A principal delas nesse momento é a pasta src que contém o código fonte do projeto. Pra facilitar nosso entendimento inicial vamos excluir todos os arquivos da pasta src e criar apenas um arquivo chamado index.js que será o nosso único arquivo nesse projeto simples.
Nesse arquivo vamos importar as duas bibliotecas bases do React que são o React e o ReactDOM. O React é a biblioteca principal do React e o ReactDOM é a biblioteca que permite interagir com o DOM. Vamos criar um componente chamado App que irá renderizar um título na tela.
import React from "react";
import ReactDOM from "react-dom/client";
function App() {
return <h1>MiniPokedex</h1>;
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);Abre o resultado no navegador e você deve ver o título MiniPokedex na tela. Pronto, você acabou de criar seu primeiro projeto com React! Nesse código fizemos o seguinte, apenas iniciamos o componente App que retorna um JSX com o título MiniPokedex e renderizamos esse componente no elemento com o id root - esse elemento está na página public/index.html. No React, todo o HTML é gerado a partir do JavaScript.
Para o desenvolvimento é interessante utilizar o <React.StrictMode> que é um componente que ajuda a escrever código mais limpo e seguro. Ele não tem efeito na produção, apenas no desenvolvimento. Para utilizá-lo basta envolver o componente
Appcom ele.
...
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);Um conceito importante de enteder é que tudo no React são componentes. Os componentes são pedaços da Interface Web que contém seus próprios dados, lógica e aparência. As páginas são compostas por esses componentes fazendo a combinação de vários deles. É possível reaproveitar os componentes, aninhar um dentro do outro e passar dados entre eles.
Para criar um componente no React é necessário criar uma função que retorna um JSX. JSX é uma extensão de sintaxe para JavaScript que permite escrever HTML dentro do JavaScript. Vamos criar um componente chamado Pokemon.
function Pokemon() {
return <h2>Pokemon</h2>;
}
function App() {
return <h1>MiniPokedex</h1><Pokemon />;
}Se você tentar rodar o código acima vai receber um erro. Isso acontece porque o React não permite que um componente retorne mais de um elemento. Para resolver isso, podemos envolver os elementos em um elemento pai.
function App() {
return (
<div>
<h1>MiniPokedex</h1>
<Pokemon />
</div>
);
}Dois detalhes importantes sobre a criação de componentes no React. O primeiro é que o nome do componente deve começar com letra maiúscula. Isso é uma convenção do React para diferenciar os componentes dos elementos HTML. O segundo detalhe é que o componente deve retornar um elemento JSX.
Para deixar o projeto mais interessante, vamos utilizar um array de objetos para representar os pokemons. Copie essa lista de objetos para dentro do seu arquivo index.js
const pokeData = [
{
id: 1,
name: "bulbasaur",
weight: 69,
image: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/1.svg",
type: "grass",
}
...
];
function Pokemon() {
return (
<div>
<img
src="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/1.svg"
alt="bulbasaur"
/>
<h2>bulbasaur</h2>
<p>electric</p>
</div>
);
}Apenas personalizamos o componente Pokemon para mostrar informações que estão no vetor de objetos. Para isso, utilizamos a interpolação de strings para acessar as propriedades do objeto. A ideia aqui é mostrar a possibilidade de reaproveitar o componente. Vamos criar uma lista com 3 pokemons, para isso, basta duplicar o componente Pokemon.
function App() {
return (
<div>
<h1>MiniPokedex</h1>
<Pokemon />
<Pokemon />
<Pokemon />
</div>
);
}React utiliza o paradigma declarativo. Para entender esse conceito imagine que você está dirigindo um carro e precisa chegar a um destino. No paradigma imperativo você diria para o carro como ele deve fazer para chegar ao destino, passo a passo. Já no paradigma declarativo você apenas diria o destino e o carro se encarregaria de chegar lá. No React você diz como a interface deve ser e o React se encarrega de fazer isso. Nesse sentido, o React é responsável por atualizar a interface quando o estado da aplicação muda e não você.
No desenvolvimento tradicional existia um conceito bastante presente chamado separação de responsabilidades na qual o HTML, CSS e Javascript deveriam estar separados. Porém com o advento das aplicações SPA (Single Page Application) essa separação começou a não fazer tanto sentido, nesse ponto o HTML e JS estavam fortemente acoplados. Nesse sentido o React e outos frameworks deixaram de necessariamente separar as tecnologias por arquivos e passou-se a unir dados, lógica e apresentação dentro de um mesmo componente. E em vez de se ter uma tecnologia por arquivo, temos um componente por arquivo.
Estilizando os componentes
Existem diversas maneiras de estilizar os componentes no React tais como inline styles, external CSS, CSS modules, styled-components, entre outros. A maneira mais simples é a utilização do inline styles.
function Header() {
return <h1 style={{ color: "red", fontSize: "24px" }}>MiniPokedex</h1>;
}Perceba que nesse exemplo utilizamos um objeto JavaScript para passar os estilos. O React utiliza a sintaxe de {{}} para passar um objeto JavaScript dentro do JSX. Vamos ver agora um exemplo de CSS externo. Para tal basta incluir o arquivo CSS no componente através da sintaxe abaixo.
import "./index.css";Props
As props (propriedades) são dados que podem ser passadas de um componente para outro. É um conceito importante e fundamental visto que agora a aplicação toda é baseada em componentes, logo de alguma maneira eles precisam se comunicar. Existem dois pontos chaves que você precisa se lembrar sobre as props:
- elas são utilizadas para passar dados de um componente pai para um componente filho e;
- são somente leitura, elas são imutáveis!
Para exemplificar vamos tornar o componente Pokemon mais dinâmico. Vamos passar as informações do pokemon como props para o componente. Veja o exemplo abaixo:
function Pokemon(props) {
return (
<div>
<img src={props.image} alt={props.name} />
<h2>{props.name}</h2>
<p>{props.type}</p>
</div>
);
}Nesse momento fizemos com que nosso componente receba as informações do pokemon através das props. Veja que as props nada mais são do que um objeto JavaScript que contém as informações que passamos para o componente. Feita essa modificação agora de fato precisamos fazer com que a chamada do componente Pokemon passe as informações respectivas. Para isso, vamos alterar novamente o código, mas agora na chamada ao componente Pokemon.
function App() {
return (
<div>
<h1>MiniPokedex</h1>
<Pokemon
name="bulbasaur"
image="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/1.svg"
type="grass"
/>
<Pokemon
name="pikachu"
image="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/25.svg"
type="electric"
/>
<Pokemon
name="charmander"
image="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/4.svg"
type="fire"
/>
</div>
);
}Rode a aplicação novamente e você verá que agora os pokemons estão sendo renderizados com as informações corretas. Perceba que agora o componente Pokemon é reutilizável, ou seja, podemos passar as informações de qualquer pokemon para ele. Isso é uma das grandes vantagens de se trabalhar com componentes no React.
Mas podemos melhorar ainda nosso exemplo. Até então estamos passando as informações dos pokemons diretamente no código digitando manualmente e como você deve imaginar, isso não é nada ideal visto que não vamos ficar alterando o código do componente toda vez que quisermos adicionar um novo pokemon. Nós já temos as informações dos pokemons em um array, então por que não utilizá-las? Para isso precisamos percorrer o array de pokemons e renderizar um componente Pokemon para cada um deles. Vamos ver como fazer isso.
Você pode estar pensando que poderíamos utilizar um loop forEach para percorrer o array de pokemons e renderizar um componente Pokemon para cada um deles. Porém, no React, não vamos conseguir o efeito desejado visto que cada iteração precisa renderizar um componente e nesse caso o forEach vai apenas percorrer e não criar ou modificar algum objeto. Em vez disso, podemos utilizar o método map() de um array para criar um novo array com os elementos renderizados. Vamos ver como fazer isso.
function App() {
return (
<div>
<h1>MiniPokedex</h1>
{pokeData.map((pokemon) => (
<Pokemon name={pokemon.name} image={pokemon.image} type={pokemon.type} />
))}
</div>
);
}Importante observar que o método map() precisa de uma chave única para cada elemento. Essa chave é utilizada pelo React para identificar cada elemento de forma única. A chave deve ser única entre os elementos irmãos, ou seja, elementos que estão no mesmo nível.
function App() {
return (
<div>
<h1>MiniPokedex</h1>
{pokeData.map((pokemon) => (
<Pokemon key={pokemon.id} name={pokemon.name} image={pokemon.image} type={pokemon.type} />
))}
</div>
);
}Você poderia estar pensando se poderíamos utilizar o forEach ao invés do map. A resposta é não. O forEach não retorna um novo array, ele apenas itera sobre os elementos do array, e nesse sentido o React não teria nada para renderizar.
Classes condicionais
Em alguns casos, precisamos renderizar um componente condicionalmente. Para isso, podemos utilizar uma estrutura condicional como if ou ternário. Vamos ver um exemplo de como fazer isso.
function Pokemon(props) {
return (
<div>
<img src={props.image} alt={props.name} />
<h2>{props.name}</h2>
<p>{props.type}</p>
{props.weight > 70 ? <p>Heavy</p> : <p>Light</p>}
</div>
);
}Ou mesmo podemos adicionar uma classe com base na condição. Existem várias maneiras de fazer isso, uma delas é utilizando as string templates do JavaScript.
function Pokemon(props) {
return (
<div>
<img src={props.image} alt={props.name} />
<h2>{props.name}</h2>
<p className={`badge ${props.type}`}>{props.type}</p>
</div>
);
}React Fragments
Quando precisamos retornar mais de um elemento no React, é necessário envolvê-los em um elemento pai. Porém, em alguns casos, não queremos adicionar um elemento extra no DOM. Para resolver esse problema, podemos utilizar React Fragments. React Fragments permitem agrupar uma lista de filhos sem adicionar nós extras ao DOM.
function App() {
return (
<>
<Header />
<Posts />
</>
);
}Anterior: Conceitos de JavaScript para React