Edit on GitHub

Pensando em React

React é, na nossa opinião, a principal maneira de construir aplicações WEB grandes e rápidas com JavaScript. Escalou muito bem para nós no Facebook e Instagram.

Uma das muitas grandes partes do React é como ele faz você pensar sobre como construir suas aplicações. Neste documento, acompanharemos o processo de construção de uma tabela pesquisável de dados de produtos usando o React.

Começo com um protótipo #

Imagine que já temos uma API JSON e um protótipo do nosso designer. O protótipo se parece com isso:

Mockup

Nossa API JSON retorna alguns dados parecidos com isso:

[
  {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
  {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
  {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
  {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
  {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
  {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
];

Passo 1: Quebre a interface de usuário em uma hierarquia de componentes #

A primeira coisa que você fazer é desenhar caixas ao redor de cada componente (e subcomponente) no protótipo e dar nomes todas elas. Seus nomes podem acabar sendo os nomes dos seus componentes React!

Mas como você sabe o que deve ser seu próprio componente? Basta usar as mesmas técnicas para decidir se você deve criar uma nova função ou objeto. Uma dessas técnicas é o princípio de responsabilidade única, que é, um componente deve idealmente fazer apenas uma coisa. Se ele acabar crescendo, ele deve ser decomposto em subcomponentes menores.

Uma vez que você está exibindo muitas vezes um modelo de dados JSON para um usuário, você vai perceber que, se seu modelo foi construído corretamente, sua interface de usuário (e portanto sua estrutura de componentes) está mapeada muito bem. Isto porque interfaces de usuários e modelos de dados tendem a aderir a mesma arquitetura de informação, o que significa que seu trabalho de separar sua interface de usuário em componentes é muitas vezes trivial. Basta dividi-los em componentes que representam exatamente uma peça do seu modelo de dados.

Component diagram

Veremos aqui que temos cinco componentes para nossa aplicação simples. Destacamos os dados que cada componente representa.

  1. FilterableProductTable (laranja): contém a totalidade do exemplo
  2. SearchBar (azul): recebe todas as entradas do usuário
  3. ProductTable (verde): exibe e filtra a coleção de dados baseado na entrada do usuário
  4. ProductCategoryRow (turquesa): exibe um cabeçalho para cada categoria
  5. ProductRow (vermelho): exibe uma linha para cada produto

Se você olhar para ProductTable, você verá que o cabeçalho da tabela (contendo os labels "Name" e "Price") não é um componente próprio. Isso é uma questão de preferência, e há um argumento a ser feito de qualquer forma. Para este exemplo, nós o deixamos como parte de ProductTable porque é parte da renderização da coleção de dados que é responsabilidade de ProductTable. No entanto, se esse cabeçalho cresce para ser complexo (isto é, se adicionar funcionalidade de ordenação), certamente faria sentido fazer seu próprio componente ProductTableHeader.

Agora que identificamos os componentes no nosso protótipo, vamos organizá-los dentro de uma hierarquia. Isto é fácil. Componentes que aparecem dentro de outro componente no protótipo devem aparecer como um filho na hierarquia.

  • FilterableProductTable
    • SearchBar
    • ProductTable
      • ProductCategoryRow
      • ProductRow

Passo 2: Crie uma versão estática em React #

Veja o código de Pensando em React: Passo 2 no CodePen.

Agora você tem a hierarquia dos seus componentes, é hora de implementar sua aplicação. A maneira mais fácil é construir uma versão que pega seus dados e renderiza a interface de usuário sem interatividade. É melhor para desacoplar estes processos porque construir uma versão estática requer digitar muito código e pensar pouco, e adicionar interatividade requer pensar muito e não muito código. Vamos ver por quê.

Para construir uma versão estática da sua aplicação que renderiza sesu dados, você vai querer construir componentes que reusam outros componentes e passam dados usando props. Props são uma maneira de passar dados de um pai para um filho. Se você é familiar com o conceito de state, não use state para construir esta versão estática. State é para reservado apenas para interatividade, que é, dados que mudam ao longo do tempo. Como esta é uma versão estática da aplicação, você não precisa dele.

Você pode construir de cima pra baixo ou de baixo para cima. Isto é, você pode tanto começar a construir os componentes mais altos na hierarquia (isto é, começar com FilterableProductTable) ou com um dos componentes mais baixos (ProductRow). Em exemplos simples, é usualmente mais fácil ir de cima para baixo, e em projetos grandes, é mais fácil ir de baixo para cima e escrever testes conforme constrói.

No final desse passo, você terá uma biblioteca de componentes reusáveis que renderam seus dados. Os componentes terão apenas o método render() uma vez que esta é uma versão estática da sua aplicação. O componente no topo da hierarquia (FilterableProductTable) terá seus dados como uma propriedade(props). Se você fizer uma alteração nos seus dados subjacentes e chamar ReactDOM.render() novamente, a interface de usuário será atualizada. É fácil ver como sua interface de usuário é atualizada e onde fazer mudanças desde que não haja nada complicado acontecendo. O fluxo de dados de unidirecional(one-way) do React (também chamado one-way binding) mamtém tudo modular e rápido.

Basta consultar a documentação do React caso precise de ajuda para executar este passo.

Um breve intervalo: Props(propriedades) vs State(estado) #

Existem dois tipos de "modelo" de dados em React: props e state. É importante entender a distinção entre os dois. Veja a documentação oficial do React se você não tem certeza de qual é a diferença.

Passo 3: Identificar a mínina (mas completa) representação do estado da interface de usuário #

Para tornar sua interface de usuário interativa, você precisa ser capaz de fazer alterações na sua camada de dados subjacente. React torna isso fácil com state.

Para construir sua aplicação corretamente, você precisa pensar no conjunto mínimo de estado mutável que sua aplicação precisa. A chave aqui é DRY Don't Repeat Yourself (Não se repita). Descubra a representação mínima do estado da sua aplicação e calcule todo resto por demanda. Por exemplo, se você está construindo uma lista de tarefas (TODO list), matenha apenas um array de TODO itens. Não tenha uma variável de estado separada para o contador. Ao invés disso, quando você quiser renderizar o contador de itens, simplesmente pegue o tamanho do array dos TODO itens.

Pense em todas os pedaços de dados na nossa aplicação de exemplo. Nós temos:

  • Uma lista original de produtos
  • Um texto de busca que o usuário digitou
  • O valor do checkbox
  • A lista filtrada de produtos

Vamos percorrer cada item e descobrir qual é o estado. Basta fazer três perguntas sobre cada pedaço de dado:

  1. É passado via props de um componente pai? Se for, provavelmente não é um estado.
  2. Permanece inalterado ao longo do tempo? Se sim, provavelmente não é um estado.
  3. Você pode calculá-lo baseado em qualquer outro estado ou propriedade no seu componente. Se sim, não é um estado.

A lista original de produtos é passada via propriedade, então não é um estado. O texto da caixa de pesquisa e o checkbox parecem ser estados, uma vez que mudam ao longo do tempo e não podem ser calculados de qualquer coisa. E finalmente, a lista filtrada de produtos não é um estado porque pode ser calculada pela combinação da lista de produtos original e o texto da caixa de pesquisa e o checkbox.

Então, finalmente, nosso estado é:

  • O texto da caixa de pesquisa que o usuário digitou
  • O valor do checkbox

Passo 4: Identificar onde seu estado deve viver #

Veja o código de Pensando em React: Passo 4 de Kevin Lacker (@lacker) no CodePen.

Certo, identificamos o conjunto mínimo de estado da aplicação. Agora, vamos identificar qual componente muda ou possui este estado.

Lembre-se: React trata o fluxo de dados de forma unidirecional (one-way), que desce hierarquia de componentes. Não deve ser imediatamente claro qual componente deve possuir qual estado. Isso é geralmente a parte mais desafiantes para recém-chegados enteder, então siga estes passos para descobrir:

Para cada pedaço de estado na sua aplicação:

  • Identifique cada componente que renderiza alguma coisa baseada no estado.
  • Encontre um componente dono comum (um componente único acima de todos os componentes que precisam do estado na hierarquia)
  • O componente dono ou outro componente mais acima na hierarquia deve possuir o estado.
  • Se você não conseguir encontrar um componente que faça sentido possuir o estado, crie um novo componente simplesmente para manter o estado e adicione-o em algum lugar na hierarquia acima do componente dono comum.

Vamos passar por essa estratégia para nossa aplicação:

  • ProductTable precisa filtrar a lista de produtos baseado no estado e SarchBar precisa exibir o texto de busca e o estado de checado do checkbox.
  • O dono comum deles é o FilterableProductTable.
  • Conceitualmente faz sentido para o texto de busca e o valor de checado do checkbox permanecerem no FilterableProductTable.

Legal, decidimos que nosso estado permanece no FilterableProductTable. Primeiro, adicione uma propriedade de instância this.state = {filterText: '', inStockOnly: false} para FilterableProductTable no seu constructor para refletir o estado inicial da sua aplicação. Então passe filterText e inStockOnly para ProductTable e SearchBar como propriedades. Finalmente, use estas propriedades para filtrar as linhas em ProductTable e configure os valores nos campos do formulário em SearchBar.

Você pode começar a ver como sua aplicação irá se comportar: configure filterText para "ball" e atualize sua aplicação. Você verá que os dados da tabelas serão atualizados corretamente.

Passo 5: Adicione fluxo de dados inverso #

Veja o código de Pensando em React: Passo 5 no CodePen.

Até agora, construímos uma aplicação que renderiza os componentes corretamente em função das propriedades e estado. Fluindo os dados para baixo pela hierarquia. Agora é hora de subir os dados na outra direção: os componentes do formulário que estão mais a fundo da hierarquia precisam atualizar o estado em FilterableProductTable.

React torna este fluxo de dados explícito para facilitar o entendimento do funcionamento da sua aplicação, porém é necessário escrever um pouco mais de código do que o tradicional ligação bidirecional de dados(two-way data binding).

Se você tentar digitar ou marcar o checkbox na versão atual do exemplo, você verá que o React igonra sua entrada. Isto é intencional, já que configuramos a propriedade value do input para sempre ser igual ao state(estado) passado de FilterableProductTable.

Vamos pensar sobre o que queremos que aconteça. Queremos ter certeza que sempre que o usuário mude o formulário, atualizamos o estado para fefletr a entrada do usuário. Uma vez que componentes devem apenas atualizar seu próprio estado, FilterableProductTable passará uma chamada de retorno (callback) para SearchBar que irá disparar sempre que o estado deva ser atualizado. Podemos usar o evento onChange do input para ser notificado dele. E o callback passado para FilterableProductTable chamará setState(), e a aplicação será atualizada.

Embora isso pareça complexo, são epenas algumas linhas de código. Fica realmente explícito como seus dados estão fluindo pela aplicação.

E é isso #

Esperançosamente, isto dá a você uma idéia de como pensar sobre a construção de componentes e aplicações com React. Embora possa ser um pouco mais de código do que você está acostumado, lembre-se que o código é lido muito mais que escrito e é extremamente fácil de ler este código explícito e modular. À medida que começar a construir grande bibliotecas de componentes, você apreciará esta explicitação e modularidade, com a reutilização de código, suas linhas de código começaram a encolher. :)