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.
Imagine que já temos uma API JSON e um protótipo do nosso designer. O protótipo se parece com isso:
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"}
];
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.
Veremos aqui que temos cinco componentes para nossa aplicação simples. Destacamos os dados que cada componente representa.
FilterableProductTable
(laranja): contém a totalidade do exemploSearchBar
(azul): recebe todas as entradas do usuárioProductTable
(verde): exibe e filtra a coleção de dados baseado na entrada do usuárioProductCategoryRow
(turquesa): exibe um cabeçalho para cada categoriaProductRow
(vermelho): exibe uma linha para cada produtoSe 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
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.
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.
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:
Vamos percorrer cada item e descobrir qual é o estado. Basta fazer três perguntas sobre cada pedaço de dado:
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 é:
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:
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.FilterableProductTable
.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.
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.
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. :)