React: do básico ao avançado - Parte 3
React é uma biblioteca JavaScript para desenvolvimento de interfaces de usuário.
Esta é a terceira parte de uma série de artigos que ensina a desenvolver e estruturar aplicações com JavaScript e React.
Clique aqui caso você ainda não tenha lido a segunda parte.
Imagino que você deve estar ansioso para escrever código, mas antes quero explicar alguns conceitos que irão ajudar a entender o que o código escrito com React realmente faz.
Que problema React soluciona?
React foi criado para resolver apenas um problema: desenvolver apps pequenas, médias ou grandes, que possuem dados que mudam ao longo do tempo.
Para atingir este objetivo, com React, você só precisa descrever como sua app deve se apresentar num determinado momento com base nos dados recebidos do usuário, browser ou back-end.
Quando algum dado muda, React simplesmente "aperta" o botão refresh e sabe como atualizar somente as partes que mudaram, ou seja, as atualizações de interface são gerenciadas automaticamente para você. 🎉
É por isso que React incentiva você a escrever código declarativo em vez de imperativo. Ao invés de dizer como fazer, você diz o que quer, e o React faz o trabalho chato pra você.
Hum. Não sei se entendi...
Certo, vou ilustrar a explicação anterior com pseudocódigo.
Se você ainda não usou React, provavelmente você só trabalhou com classes e instâncias. Por exemplo, com o Backbone.js, você pode criar um componente Button
criando uma classe. Quando a app estiver rodando, instâncias do componente Button
serão criadas, cada uma com suas próprias propriedades.
Neste modelo tradicional, cabe a você criar, atualizar e destruir instâncias de componentes filhos. Se um componente Form
deseja renderizar um Button
, ele precisa criar uma instância do Button
, manter a instância atualizada após o recebimento de novos dados e destruí-la quando não precisar mais dela.
Segue uma pseudo implementação do componente Form
:
class Form extends TraditionalView { render() { if (!this.submitted && !this.button) { // Form ainda não foi submetido, // e uma instância de Button ainda não foi criada. // Vamos criar uma instância de Button, this.button = new Button() // adicioná-la a este Form, this.el.appendChild(this.button.el) // definir alguns atributos, this.button.attrs.type = 'primary' this.button.attrs.label = this.attrs.buttonLabel // e por fim, renderizar a instância criada. this.button.render() } if (this.submitted && this.button) { // Form foi submetido, // e uma instância de Button foi criada. // Vamos remover a instância de Button deste Form, this.el.removeChild(this.button.el) // e destruí-la para limpar a memória. this.button.destroy() } if (this.submitted && !this.message) { // Form foi submetido, // e uma instância de Message ainda não foi criada. // Vamos criar uma instância de Message, this.message = new Message() // adicioná-la a este Form, this.el.appendChild(this.message.el) // definir um atributo, this.message.attrs.content = 'Sucesso!' // e por fim, renderizar a instância criada. this.message.render() } } }
Este é um pseudocódigo e não vai funcionar. Como dito, isto é apenas pseudocódigo, porém é mais ou menos o que você escreve quando usa bibliotecas tradicionais como o Backbone.js. No modelo tradicional, cada instância de um componente precisa manter sua referência no DOM, além de criar, atualizar e destruir instâncias de componentes-filhos. O código cresce de forma absurda caso o componente pai possua diversos componentes filhos ou diversos tipos de estado. Além disso, como os componentes-pai possuem acesso direto às instâncias dos componentes filhos, é extremamente difícil de desacoplá-los no futuro. Como React é diferente? No React foi criado o conceito de elementos. Um elemento é um objeto que descreve uma instância e suas propriedades. Essa instância pode ser um componente, uma tag HTML ou uma tag SVG. É importante entender que, na verdade, um elemento não é uma instância, ao invés disso, ele é apenas a forma que você diz para o React que quer renderizar algo na tela, por isso elementos não possuem métodos e são imutáveis. Elementos são, nada mais, nada menos, que objetos com apenas duas propriedades: type
e props
. Quando type
é uma string, o elemento representa uma tag HTML ou SVG e seus atributos. Segue um exemplo: const myAwesomeInput = { type: 'input', props: { type: 'text', value: '🖊🍍🍎🖊' } } // myAwesomeInput representa a seguinte estrutura HTML: <input type="text" value="🖊🍍🍎🖊" />
Este é um pseudocódigo e não vai funcionar. Nota: se você ficou curioso para descobrir o que é 🖊🍍🍎🖊, assista este vídeo. :-) No exemplo acima, type
define a tag HTML e props
seus atributos. Como elementos, na verdade, são apenas objetos que descrevem uma instância, eles são muito mais leves que instâncias verdadeiras. Além de uma string, você pode passar um componente que você criou para type
, e esta, é a ideia principal do React. Elementos que descrevem um componente criado por você, são como elementos que descrevem uma tag HTML ou SVG. Não há diferença. Eles podem ser aninhados e combinados. Esta funcionalidade permite que você crie um componente chamado Button
com uma propriedade color
específica, sem se procupar se o Button
vai renderizar um button
, um div
ou qualquer outra coisa. Segue um exemplo da definição e uso do componente Button
: function Button(props) { return { type: 'button', props: { style: { color: props.color }, children: props.children } } } const myAwesomeButton = { type: Button, props: { color: 'blue', children: '🖊🍍🍎🖊' } } // myAwesomeButton representa a seguinte estrutura HTML: <Button color="blue"> 🖊🍍🍎🖊 </Button> // ...que quando for renderizada pelo React, // vai se transformar em: <button style="color: blue;"> 🖊🍍🍎🖊 </button >
Este é um pseudocódigo e não vai funcionar. Você notou que usamos uma propriedade chamada children
. Esta propriedade nativa do React foi criada para que o elemento-pai possa receber um ou mais elementos-filhos. É através dela que podemos criar árvores de elementos. Não vamos entrar em detalhes sobre a propriedade children
agora, o importante é ter em mente que no exemplo anterior, passamos uma string para children
, e o React sabe como renderizá-la corretamente. Ao invés de uma string, poderíamos também ter passado um ou mais elementos, optamos por uma string porque queremos mostrar um texto básico como conteúdo do Button
. Agora que sabemos como elementos funcionam, podemos reescrever nosso Form
utilizando React e o conceito de elementos. Segue uma pseudo implementação: function Form(props) { if (props.submitted) { // Form foi submetido, // vamos pedir para o React renderizar // um elemento do tipo Message // com uma propriedade return { type: Message, props: { content: 'Sucesso!' } } } // Form não foi submetido, // vamos pedir para o React renderizar // um elemento do tipo Button // com algumas propriedades return { type: Button, props: { type: 'primary', label: props.buttonLabel } } }
Este é um pseudocódigo e não vai funcionar. Como você pode perceber, nós delegamos para o React a criação, atualização e destruição de instâncias. Apenas dizemos para o React de forma declarativa o que queremos, e ele se encarrega de gerenciar as instâncias. Nota: as explicações e exemplos foram baseadas no artigo React Components, Elements, and Instances criado pelo Dan Abramov. Resumo Nesta parte da série, aprendemos o que React soluciona e como ele soluciona. Na quarta parte desta série, vamos falar mais sobre a propriedade children
e como criar elementos.