DEV Community

Cover image for React: Guia Visual para o Modelo Mental do React
Eduardo Rabelo
Eduardo Rabelo

Posted on

React: Guia Visual para o Modelo Mental do React

Aprendi que a maior diferença entre alguém que domina uma linguagem, estrutura ou ferramenta e alguém que não são os modelos mentais que elas usam. Uma pessoa terá um modelo claro e avançado e a outra não.

Por ter um bom modelo mental, você pode entender intuitivamente problemas complexos e soluções de dispositivos muito mais rapidamente do que se tivesse que encontrar uma solução com um processo passo a passo.

Trabalho com a React todos os dias e estou constantemente encontrando soluções para problemas desafiadores. Eu posso fazer isso tendo bons modelos mentais em torno do React. Neste artigo, explicarei os modelos mentais que me ajudam a resolver problemas e a domar a complexidade.

Se você trabalha com o React há anos ou está apenas começando, ter um modelo mental útil é, na minha opinião, a maneira mais rápida de se sentir confiante trabalhando com ele.

O Que É Um Modelo Mental?

Um modelo mental é como imaginamos que um sistema funcione. Criamos um entendendo diferentes partes do sistema e suas conexões, e é importante porque nos ajuda a entender o mundo e a resolver problemas.


Uma representação visual de um modelo mental

Um bom exemplo de modelo mental é a internet: é um sistema complexo com muitas partes interconectadas, mas pense na maneira como você imagina que ela funcione. Eu imagino isso como muitos computadores conectados entre si através de muitos servidores grandes, com muitos intermediários redirecionando onde cada informação é armazenada.

É claro que esse é um modelo mental incompleto, mas é bom o suficiente para que eu possa trabalhar com ele, resolver problemas e melhorá-lo se eu precisar, e essa é a essência: modelos mentais são criados para nos ajudar a resolver problemas e entender o mundo.

Por Que Os Modelos Mentais São Importantes?

Quando comecei a criar sites em 2014, tive dificuldade em entender como tudo funcionava. Construir meu blog com WordPress foi fácil, mas eu não tinha idéia de hospedagem, servidores, DNS, certificados e muito mais.

Enquanto eu lia artigos e experimentava coisas (e quebrei a configuração do servidor mais de uma vez), comecei a entender o sistema, a ter vislumbres de como tudo funcionava, até que, eventualmente, "clicou" e me senti confortável trabalhando com ele. Minha mente havia construído um modelo mental em torno desse sistema que eu poderia usar para trabalhar com ele.

Se alguém tivesse explicado isso, transferido seu modelo mental para mim, eu o entenderia muito mais rápido. Aqui vou explicar (e mostrar) os modelos mentais que uso com o React. Isso o ajudará a entender o React melhor e a torná-lo um desenvolvedor melhor.

Modelo Mental do React

O React nos ajuda a criar interfaces de usuário complexas e interativas com mais facilidade do que nunca. Também nos incentiva a escrever código de uma certa maneira, guiando-nos a criar aplicativos mais simples de navegar e entender.

O React em si é um modelo mental com uma idéia simples: encapsula partes do seu aplicativo que dependem de lógica e interface de usuário semelhantes e o React garante que essa parte esteja sempre atualizada.

Se você trabalha com o React há anos ou está apenas começando, ter um modelo mental claro é a melhor maneira de se sentir confiante trabalhando com ele. Então, para eu transferir meus modelos mentais para você, vou começar pelos primeiros princípios e construir sobre eles.

Funções Em Todo O Lugar

Vamos começar modelando os blocos de construção básicos das funções JavaScript e React:

  • Um componente React é apenas uma função
  • Componentes contendo outros componentes são funções que chamam outras funções
  • Props são os argumentos da função

Isso está oculto pelo JSX, a linguagem de marcação que o React usa. Afastando o JSX, React é um monte de funções que chamam umas as outras. O JSX é em si é um modelo mental aplicado que torna o uso do React mais simples e intuitivo.

Vamos olhar para cada parte individualmente.

Um Componente É Uma Função Que Retorna JSX

O React é usado com JSX - JavaScript ​​XML - uma maneira de escrever o que parece HTML com todo o poder do JavaScript. O JSX oferece um ótimo modelo mental aplicado para usar funções aninhadas de uma maneira que pareça intuitiva.

Vamos ignorar os componentes de classe e focar nos componentes funcionais muito mais comuns. Um componente funcional é uma função que se comporta exatamente como qualquer outra função JavaScript. Os componentes do React sempre retornam JSX, que é então executado e transformado em HTML.

É assim que o JSX se parece:

const Li = props => <li {...props}>{props.children}</li>;

export const RickRoll = () => (
  <div>
    <div className='wrapper'>
      <ul>
        <Li color={'red'}>Never give you up</Li>
      </ul>
    </div>
  </div>
);

Compilado em JavaScript puro por Babel:

const Li = props => React.createElement('li', props, props.children);

export const RickRoll = () =>
  React.createElement(
    'div',
    null,
    React.createElement(
      'div',
      {
        className: 'wrapper',
      },
      React.createElement(
        'ul',
        null,
        React.createElement(
          Li,
          {
            color: 'red',
          },
          'Never give you up',
        ),
      ),
    ),
  );

Se você acha difícil seguir o código cima, você não está sozinho, e entenderá por que a equipe do React decidiu usar o JSX.

Agora, observe como cada componente é uma função que chama outra função e cada novo componente é o terceiro argumento para a função React.createElement. Sempre que você escreve um componente, é útil ter em mente que é uma função JavaScript normal.

Uma característica importante do React é que um componente pode ter muitos filhos, mas apenas um pai. Achei isso confuso até perceber que é a mesma lógica que o HTML possui, onde cada elemento deve estar dentro de outros elementos e pode ter muitos filhos. Você pode perceber isso no código acima, onde há apenas um pai div contendo todos os filhos.

Props Do Componente São Os Mesmos Que Os Argumentos De Uma Função

Ao usar uma função, podemos usar argumentos para compartilhar informações com essa função. Para os componentes React, chamamos esses argumentos de props (história engraçada, eu não percebi que props é a abreviação para properties por um longo tempo).

Sob o capô, as props se comportam exatamente como argumentos de função, as diferenças são que interagimos com eles por meio da interface mais agradável do JSX, e que o React fornece funcionalidade extra para props como children.

Criando Um Modelo Mental Em Torno De Funções

Usando esse conhecimento, vamos criar um modelo mental para entender intuitivamente as funções!

Quando penso em uma função, imagino-a como uma caixa, e essa caixa fará algo sempre que for chamada. Pode retornar um valor ou não:

function sum(a, b) {
  return a + b;
}

console.log(sum(10, 20)); // 30

function logSum(a, b) {
  console.log(a + b); // 30
}

Como um componente é uma "função chique", isso também torna um componente uma caixa, com props sendo os ingredientes necessários para criar a saída.

Quando um componente é executado, ele executa qualquer lógica que ele tenha, se houver, e avalia seu JSX. Qualquer tag se tornará HTML e qualquer componente será executado, e o processo é repetido até atingir o último componente na cadeia de filhos.

Como um componente pode ter muitos filhos, mas apenas um pai, imagino vários componentes como um conjunto de caixas, um dentro do outro. Cada caixa deve estar dentro de uma caixa maior e pode ter muitas caixas menores dentro.

Mas o modelo mental de uma caixa que representa um componente não está completo sem entender como ele pode interagir com outras caixas.

Como Pensar Em Closures

Closures são um conceito central em JavaScript. Elas permitem funcionalidades complexas na linguagem, são super importantes para entender a fim de ter um bom modelo mental em torno do React.

Elas também são um dos recursos que os recém-chegados enfrentam mais dificuldade; portanto, ao invés de explicar os detalhes técnicos, demonstrarei o modelo mental que tenho em relação a Closures.

A descrição básica de uma closure é que é uma função. Eu a imagino como uma caixa que impede que o que está dentro dela se espalhe, enquanto permite que coisas externas entrem em seu contexto, como uma caixa semi-permeável. Mas espalhar para onde?

Embora a closure em si seja uma caixa, qualquer closure estará dentro de caixas maiores, com a caixa mais externa sendo o objeto Window.


O objeto Window encapsula tudo o resto

Mas O Que É Uma Closure?

Uma closure é um recurso das funções JavaScript. Se você estiver usando uma função, está usando closure.

Como mencionei, uma função é uma caixa e isso também a torna em uma closure. Considerando que cada função pode conter muitas outras dentro dela, a closure é a capacidade de uma função de usar as informações externas, mantendo suas informações internas, sem “vazar” essas informações ou permitir de serem usadas pelas funções externas.

Falando em termos do meu modelo mental: imagino as funções como caixas dentro de caixas, e cada caixa menor pode ver as informações da caixa externa, ou pai, mas a caixa grande não pode ver as informações da menor. Essa é a explicação mais simples e precisa de closures que posso fazer.


Cada função pode acessar apenas suas próprias informações e as informações dos pais

As closures são importantes porque podem ser exploradas para criar algumas mecânicas poderosas e o React tira proveito disso.

Closures em React

Cada componente React também é uma closure. Nos componentes, você só pode transmitir props de pai para filho e o pai não pode ver o que há dentro do filho. Esse é um recurso destinado a tornar o fluxo de dados do aplicativo mais simples de rastrear. Para descobrir de onde vêm os dados, geralmente precisamos subir na "árvore de componentes" para descobrir qual pai está enviando essas informações.

Um ótimo exemplo de closure no React é a atualização do estado dos pais por meio de um componente filho. Você provavelmente já fez isso sem perceber que estava brincando com closures.

Para começar, sabemos que o pai não pode acessar as informações do filho diretamente, mas o filho pode acessar o pai. Então, enviamos essas informações de pai para filho através de props. Nesse caso, as informações assumem a forma de uma função que atualiza o estado do pai.

const Parent = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      The count is {count}
      <div>
        <ChildButtons onClick={setCount} count={count} />
      </div>
    </div>
  );
};

const ChildButtons = props => (
  <div>
    <button onClick={() => props.onClick(props.count + 1)}>
      Increase count
    </button>
    <button onClick={() => props.onClick(props.count - 1)}>
      Decrease count
    </button>
  </div>
);

Quando um onClick acontece no button, isso executará a função recebida de props props.onClick e atualizará o valor usando props.count.

A idéia aqui está na maneira como atualizamos o estado de um pai por meio de um filho, neste caso a função props.onClick. O motivo disso funcionar é porque a função foi declarada dentro do escopo do componente Parent, dentro de sua closure, possibilitando que componentes filhos tenham acesso às informações do pai. Mesmo quando essa função é chamada em um filho, ela ainda vive na closure do pai.

Isso pode ser difícil de entender, então da maneira que eu imagino é como um "túnel" entre as closures. Cada um tem seu próprio escopo, mas podemos criar um túnel de comunicação unidirecional que conecta os dois.

Depois que entendermos como as closures afetam nossos componentes, podemos dar o próximo grande passo: Estados em React.

Colocando O Estado Do React Ao Nosso Modelo Mental

A filosofia do React é simples: lida com quando e como renderizar elementos, e os desenvolvedores controlam o que renderizar. Estado é a nossa ferramenta para decidir o que.

Quando o estado muda, seu componente é renderizado e, portanto, re-executa todo o código. Fazemos isso para mostrar informações novas e atualizadas ao usuário.

No meu modelo mental, o estado é como uma propriedade especial dentro da caixa. É independente de tudo o que acontece dentro dela. Ele obterá um valor padrão na primeira renderização e sempre estará atualizado com o valor mais recente.

Cada variável e função é criada em cada renderização, o que significa que seus valores também são novos. Mesmo que o valor de uma variável nunca seja alterado, ele é recalculado e reatribuído, sempre. Esse não é o caso do estado, ele muda apenas quando há um pedido para que ele mude por meio de um evento.


Estado é uma parte especial e independente da caixa; com props vindos de fora

O estado segue uma regra simples: sempre que muda, o componente e seus filhos são renderizados novamente. As props seguem a mesma lógica; se uma prop for alterada, o componente será renderizado novamente; no entanto, podemos controlar o estado, modificando-o, as props são mais estáticas e geralmente mudam como reação a uma mudança de estado.

O Modelo Mental De Renderização: Compreendendo A Magia Do React

Considero a renderização a parte mais confusa do React, porque muitas coisas acontecem durante a renderização que às vezes não são óbvias olhando o código. É por isso que ter um modelo mental claro ajuda.

A maneira como imagino a renderização com minhas caixas imaginárias é duas etapas: a primeira renderização cria a caixa, é quando o estado é inicializado. A segunda parte é quando ela é renderizada novamente, essa é a caixa que está sendo reciclada, a maioria é nova, mas alguns elementos importantes dela permanecem, ou seja, estado.

Em cada renderização, tudo dentro de um componente é criado, incluindo variáveis ​​e funções, é por isso que podemos ter variáveis ​​armazenando os resultados de um cálculo, pois elas serão recalculadas em cada renderização. É também por isso que as funções não são confiáveis ​​como valores, devido à sua referência (o valor da função em si) ser diferente a cada renderização.

const Thumbnail = props => (
  <div>
    {props.withIcon && <AmazingIcon />}
    <img src={props.imgUrl} alt={props.alt} />
  </div>
);

O exemplo acima dará um resultado diferente, dependendo das props que o componente recebe. O motivo pelo qual o React deve renderizar novamente a cada alteração de prop é para manter o usuário atualizado com as informações mais recentes.

No entanto, o estado não muda nas novas renderizações, seu valor é mantido. É por isso que a caixa é "reciclada", ao invés de criar uma totalmente nova. Internamente, o React mantém o controle de cada caixa e garante que seu estado seja sempre consistente. É assim que o React sabe quando atualizar um componente.

Imaginando uma caixa sendo reciclada, posso entender o que está acontecendo dentro dela. Para componentes simples, é fácil entender, mas quanto mais complexo um componente se torna, mais props ele recebe, mais estado mantém, mais útil se torna um modelo mental claro.

Um Modelo Mental Completo Do React: Reunindo Tudo

Agora que expliquei todas as diferentes partes do quebra-cabeça separadamente, vamos juntar tudo. Aqui está o modelo mental completo que eu uso para componentes React, traduzido diretamente de como os imagino em palavras.

Imagino um componente React como uma caixa que contém todas as informações dentro de suas paredes, incluindo seus filhos, que são mais caixas.

E, como uma caixa no mundo real, ela pode ter outras caixas e essas caixas, por sua vez, podem conter mais caixas. Dessa forma, cada caixa / componente deve ter um único pai e um pai pode ter muitos filhos.


A representação básica de um componente React

As caixas são semi-permeáveis, o que significa que nunca vazam nada para o exterior, mas podem usar informações de fora como se pertencessem a elas. Eu as imagino assim para representar como as closures funcionam em JavaScript.

Em React, a maneira de compartilhar informações entre os componentes é chamada props, a mesma idéia se aplica à função com seus arguments, ambos funcionam da mesma maneira, mas com uma sintaxe diferente.

Dentro dos componentes, as informações só podem ser transmitidas dos pais para os filhos. Em outras palavras, os filhos podem acessar os dados e o estado dos pais, mas não o contrário, e a maneira como compartilhamos essas informações é através das props.

Eu imagino esse compartilhamento direcional de informações como caixas dentro de caixas. Com a caixa mais interna sendo capaz de absorver os dados dos pais.


Os dados são compartilhados de pai para filho

Porém, a caixa deve ser criada primeiro, e isso acontece no render quando o valor padrão é atribuído ao state, assim como nas funções, todo o código dentro do componente é executado. No meu modelo mental, isso é equivalente à caixa que está sendo criada .

Renderizações subseqüentes, ou re-renders, executa todo o código no componente novamente, recalculando variáveis, recriando funções e assim por diante. Tudo, exceto state é novo em cada renderização. O valor do estado é mantido entre as renderizações e é atualizado apenas por meio de um método set.

No meu modelo mental, vejo a re-renderização como reciclando a caixa, pois a maioria é recriada, mas ainda é a mesma caixa devido ao React acompanhar o estado do componente.

Quando uma caixa é reciclada, todas as caixas dentro dela, seus filhos, também são recicladas. Isso pode acontecer porque o estado do componente foi modificado ou uma prop alterada.


Modelo mental de uma nova renderização do componente React quando props ou estados mudam

Lembre-se de que uma alteração de estado ou prop significa que as informações que o usuário vê estão desatualizadas e o React sempre deseja manter a interface do usuário atualizada, para renderizar novamente o componente que deve mostrar os novos dados.

Ao usar esses modelos mentais, me sinto confiante ao trabalhar com o React. Eles me ajudam a visualizar o que pode ser um labirinto de código em um mapa mental abrangente. Também desmistifica o React e o eleva a um nível com o qual estou muito mais confortável.

React não é tão complexo assim que você começa a entender os princípios básicos por trás dele e cria algumas maneiras de imaginar como seu código funciona.


Espero que este artigo tenha sido útil para você e tenha sido tão agradável de ler quanto de escrever! Percebi que entendo React intuitivamente e colocar esse entendimento em palavras foi um desafio.

Algumas das explicações fornecidas neste artigo são muito simplificadas, por exemplo, mais coisas não são executadas novamente em cada renderização, como os hooks useEffect, useCallback e useMemo. Meu modelo mental completo é mais complexo do que eu poderia explicar em um único artigo, fique atento às PARTES 2 e 3.

A Parte 2 se concentrará em um modelo detalhado da API do React, como useMemo, useCallback e useEffect, além de como usar um modelo mental para melhorar o desempenho do seu aplicativo React. A Parte 3 se concentrará em recursos de alto nível, como Context e um resumo do modelo mental exato e completo que eu uso para o React.

Se você quiser ler as próximas duas partes, considere assinar a minha newsletter, novos artigos chegam primeiro por e-mail.


Créditos

Oldest comments (0)