[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:
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.
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
Gostei bastante do post. Acredito que ele seja voltado para um publico mais iniciante no React, e portanto senti falta de uma explicação melhor sobre os estados, principalmente sobre o hook useState, e o que são esses valores (isRonaldo, setIsRonaldo), qual a responsabilidade de cada um.
Só para fazer uma tangente no assunto de keys no React, há um tempo atrás eu fiz um post aqui falando justamente sobre isso. https://www.tabnews.com.br/matheuspazinati/a-importancia-da-key-prop-para-renderizacao-de-listas-no-react
Bem legal a explicação. Recentemente precisei desenvolver um tema para Magento 2 PWA, que é baseado em React, e meu contato com React era zero. Sofri bastante as consequências de não saber esses princípios, e só aprendi a duras penas.
Gostei da explicação, mas podia ter explicado porque o conter precisou do setCounter pra funcionar. Sou do PHP então não entendi o motivo dessa mudança.
Esses dias mesmo eu tava com curiosidade sobre isso: Virtual DOM. Li a documentação e fiquei ainda mais confuso do que entendi de fato kkkk.
Eu até entendo o conceito e etc, mas na prática, como isso realmente funciona? Como posso "ver" a VDOM? Como posso criar uma VDOM? Como funciona por debaixo dos panos? Isso ainda me pega.
Achei o conteúdo bem didático, parabéns! Sei que você não abordou cada detalhe de tudo, mas pra mim que já trabalho com React, essa leitura foi uma experiência bem positiva.
Sou dev junior e comecei a trabalhar com React sabendo só javascript e golang. Então React é outro rolê... E demorei quase um mês pra entender o conceito de "key", por exemplo. Queria ter lido esse conteúdo já no primeiro dia de trabalho ahahah.
Obrigada por compartilhar seu conhecimento!
Achei bastante interessante pois reflete os problemas que costumo ter na renderiazção de componentes me react, creio que ganhei uma nova visão sobre a utilização e gestão de states
Parabéns pelo post. Conteúdo direto, simples e de fácil entendimento. Me entristece muito ver muita gente ensinando várias coisas e sempre pulando conceitos simples, porém, de extrema importância como esse.
muito obrihado pelo post! consegui entender de forma clara e rápida pontos que sempre tive dúvidas. já quero a próxima explicação
Gostei bastante! Falta conteúdos assim. Parabéns pela didatica!
Esperando os próximos!
Conteúdo muito bem rico e detalhado! Bom demais!