Executando verificação de segurança...
23

[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:

HTML example DOM tree

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:

Virtual DOM

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).


Counter Without State Gif

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:


Counter With State Gif

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:


Person Counter Without Key Gif

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>
  );
}
Person Counter With Key Gif

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! 😁


Carregando publicação patrocinada...
1

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

1

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.

1

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.

1

Olá VergilSparda, blz?

O primeiro código de exemplo tbm é um exemplo válido. Como foi mecionado na citação a abaixo o console mudando exatamente como esperado. Porém, o talvez o comportamento esperado para tela, que ao clicar em + ou - menos diminuar o valor E modifique na tela, não esteja correto.

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.

useState é um React Hook que permite adicionar uma variável de estado ao seu componente.

A convenção é nomear variáveis​de estado como [algo, setAlgo] usando desestruturação de array.

No caso do exemplo foi usado um contador, para atualizar o valor em tela

const [counter, setCounter] = useState(0);

Dessa forma mencionado, quando é o usado setAlgo que é disponibilizado do Hook do React o componente faz uma atulização da tela dessa forma mostrado o novo valor em atual memória.

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).

Diferente do primeiro exemplo onde o valor que aperece em tela é sempre o valor mostrado da primeira renderição do componetente. Depois que é feita operção counter = counter + 1; o valor tbm é modificando em memória, tanto que aprece correto no console.log, contudo não exite nova renderização de tela para mostra o novo valor.

Espero ter ajudo

0
1

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.

1

eae cssgabriel, tudo bem?

Acho que não tem como você efetivamente ver ela, porque ela fica salva na memória do próprio browser e não efetivamente "desenha" algo em tela, por isso ela tem esse "poder" de acabar sendo leve e rápida, porque, por debaixo dos panos, a VDOM não passa de milhares de objetos em que cada um representa um elemento e seus filhos.

Exemplo:

Virtual DOM Obj

Então, por debaixo dos panos o que acontece é a implementação de um algoritmo de diffing chamado reconciliação, que vai fazer a comparação entre os objetos e verificar se houve mudança, após isso a DOM real é re-renderizada com a nova atualização.

Na questão de criar uma VDOM não sei onde se encaixaria a não ser criar um novo framework front-end, porque hoje as libs cuidam muito bem disso.

Deixo aqui algumas leituras complementares
Documentação React sobre reconciliação
Post do blog logrocket (foi de onde obtive essa imagem acima)
Documentação Vue.js sobre Virtual DOM

1

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!

1

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

0
0
-1