[CONTEÚDO] Como eu gostaria que tivesse sido minha primeira aula de React
Cara, React de primeira vista é confuso sim, e se você não entender muito bem o que acontece debaixo dos panos nesse framework, provavelmente você vai achar bem complicado os conceitos mais avançados. Então, dá um ligue nesse conteúdo massa de uma tentativa de simplificar algumas coisas pra você!
Antes de qualquer código que eu fosse escrever em React, eu gostaria de ter aprendido esse próximo conceito teórico, super simples, mas que mudou minha visão sobre o framework.
1. DOM vs Virtual DOM
DOM significa Document Object Model, que em tradução livre seria Modelo de Documento por Objetos, mas simplificando, é uma maneira de estruturar o HTML em formato de “árvore”, com vários galhos, onde cada nó é um elemento. Olha a comparação entre o HTML, à esquerda, com a árvore correspondente a direita:
Show! O primeiro passo foi dado, você já sabe o que é DOM e a sua estrutura.
Mas você já deve ter ouvido que o React tem a sua própria forma de manipular a DOM, não? E como ele faz isso? Criando algo chamado de Virtual DOM, são duas cópias da DOM real, a “atualizada” e a “inicial”. Ele cria essas cópias pra poder controlar a árvore, controlando quando a DOM real realmente precisa de atualização ou não. O fluxo é o seguinte:
A Virtual DOM “inicial” é a primeira a receber a mudança em si, logo depois é comparada com a “atualizada” e, constatando que realmente teve uma mudança, ela engatilha o que chamamos de “mudança de estado” (falaremos disso mais pra frente), e por final, atualiza o elemento na DOM real, olha como é simples:
2. Estado
Acho que aqui algumas coisas já começam a ficar confusas, o que é estado? ciclo de vida? e etc. Calma aí, acho que vamos descomplicar isso agora!
Antes de realmente explicar o que é estado, queria te mostrar um código em React, e queria que você pensasse se ele vai funcionar ou não:
import React from "react";
export default function Home() {
let counter = 0;
function increaseCounter() {
counter = counter + 1;
console.log(counter);
}
function decreaseCounter() {
counter = counter - 1;
console.log(counter);
}
return (
<>
<button onClick={() => decreaseCounter()}>-</button>
{counter}
<button onClick={() => increaseCounter()}>+</button>
</>
);
}
A resposta é não, o código não vai funcionar. Mas se você rodar o código e ver o console, você vai perceber que a variável counter
está mudando exatamente como esperado, porque então, não é mostrado na tela o conteúdo atualizado? Porque o componente “Home” precisa ser re-renderizado, e só assim o novo valor de counter
poderá ser exibido em tela, e o React simplesmente não entendeu que isso precisa acontecer, e portanto não fez. Mas como faz pra ele entender então? Usando o state. O state vai engatilhar a re-renderização e controlar o ciclo de vida do componente, a tal mudança de estado, comentado no item anterior (dá uma olhada no gif do item 1).
Agora vamos mudar o código para que a variável counter seja um estado:
import React, { useState } from "react";
export default function Home() {
const [counter, setCounter] = useState(0);
function increaseCounter() {
setCounter(counter + 1);
console.log(counter);
}
function decreaseCounter() {
setCounter(counter - 1);
console.log(counter);
}
return (
<>
<button onClick={() => decreaseCounter()}>-</button>
{counter}
<button onClick={() => increaseCounter()}>+</button>
</>
);
}
Pronto! Agora o código funciona e o componente é re-renderizado. Como estamos usando o estado, o React entende que o elemento mudou e assim engatilha a renderização, sendo o setCounter
o responsável por cuidar disso por debaixo dos panos, massa né? Mas se agora você for ver o console no devtools, vai perceber que o log parece meio atrasado, mesmo tendo setado o counter para counter + 1
, o log aparece o valor antigo:
Isso acontece porque setState só vai começar o “change state” quando acabar o escopo da função em que ele foi chamado, como o console está sendo chamado dentro do mesmo escopo, então quer dizer que o estado ainda não foi mudado. Agora você já sabe como não cair mais nesse bug.
3. Keys no React
“Pô, você tava falando de DOM, renderização, estado e agora vai falar de lista?” não é bem assim.
A prop key
do React está intimamente ligada a renderização, e apesar de ser apresentada na documentação somente quando as listas estão sendo ensinadas, eu queria te mostrar uma visão que talvez você não tenha visto antes, sobre o mesmo tema. Antes, fica aqui um exemplo da documentação de como normalmente a key
é implementada:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
Agora para a visão que quero te mostrar, eu peguei o componente que criamos no item 2 e o separei em um componente exclusivo pra ele, só que agora ele recebe um nome e expõe esse nome na tela em uma tag <h4>
:
import React, { useState } from "react";
export default function Counter({ name }: { name: string }) {
const [counter, setCounter] = useState(0);
function increaseCounter() {
setCounter(counter + 1);
}
function decreaseCounter() {
setCounter(counter - 1);
}
return (
<>
<h4>{name}</h4>
<button onClick={() => decreaseCounter()}>-</button>
{counter}
<button onClick={() => increaseCounter()}>+</button>
</>
);
}
Já na Home eu modifiquei para que tivéssemos 2 “Counters” diferentes, dependendo da pessoa que eu escolher:
import React, { useState } from "react";
import Counter from "@/resources/Counter";
export default function Home() {
const [isRonaldo, setIsRonaldo] = useState(true);
return (
<div>
{isRonaldo ? <Counter name="Ronaldo" /> : <Counter name="Bia" />}
<br />
<button onClick={() => setIsRonaldo((prev) => !prev)}>Trocar</button>
</div>
);
}
Seguindo a lógica, se eu tenho um counter para cada pessoa, quer dizer que quando eu apertar em “trocar”, o valor que deve aparecer deve ser o último que foi atualizado para aquela pessoa, ou seja, se eu incrementar em 2 o counter do “Ronaldo”, quando eu trocar pra “Bia” o counter deve estar zerado, mas olha o resultado:
Agora olha o que acontece se simplesmente adicionarmos uma “key” em cada counter:
import Counter from "@/resources/Counter";
import React, { useState } from "react";
export default function Home() {
const [isRonaldo, setIsRonaldo] = useState(true);
return (
<div>
{isRonaldo ? (
<Counter key="Ronaldo" name="Ronaldo" />
) : (
<Counter key="Bia" name="Bia" />
)}
<br />
<button onClick={() => setIsRonaldo((prev) => !prev)}>Trocar</button>
</div>
);
}
PS: o counter só não se manteve o mesmo quando voltamos ao “Ronaldo” porque não estamos persistindo esse valor em algum lugar, como no Local Storage por exemplo, então ele acaba sendo zerado toda vez que é re-renderizado.
Mas percebeu como o simples fato de colocarmos uma key
fez com que o React se comportasse como o esperado? Fazendo com que os dois componentes fossem totalmente diferentes entre si. Essa é a verdadeira função da key
, o React precisa achar alguma maneira de localizar o componente em tela para que ele possa efetuar qualquer mudança, se ele não encontra diferença entre eles, então fica implícito que na realidade é o mesmo componente, por isso essa prop é usada em listas, porque é a maneira que ele vai fazer pra identificar qual elemento da lista precisa ser modificado.
Portanto, não use index como valor da key, é um valor fácil de ser repetido se você acabar utilizando duas listas diferentes na mesma tela, use um valor único e que com certeza nunca irá se repetir.
Bug bizarro que aconteceu comigo
Uma vez tive que trabalhar com várias tabelas diferentes em uma mesma página, e se você não tomar cuidado com a key, a tabela se torna algo muito fácil do React se confundir entre as linhas, qual linha é qual, por exemplo. E foi exatamente isso o que aconteceu, a mesma key acabou se repetindo em duas tabelas diferentes, e quando acionávamos o botão para mudar algo naquela linha, a tabela simplesmente mudava a ordem das linhas, mas não realizava nada da ação que queríamos. Depois de **muito** debugar, um amigo foi inspecionar a key, já como última opção, e acabou se deparando com duas keys iguais, ao ajustar os valores a ação ocorreu como esperado. E sim, por algum acaso o React não avisou que tínhamos keys repetidas 🥲Se você pegou algum erro nesse post, ou se deseja complementar o que foi apresentado, por favor deixa um comentário aqui embaixo, eu com certeza vou ler e tentar aprender ao máximo com ele!
Esse é um teste pra começar a escrever para blogs, espero que tenham gostado! 😁
-
Referências
Conteúdos de aulas do youtube dos canais RocketSeat e Web Dev Simplified