React.js View State Management Tutorial

Publicados: 2022-03-11

Um dos maiores e mais comuns problemas no desenvolvimento web front-end é o gerenciamento de estado. Desenvolvedores front-end freelance como eu estão constantemente focados em manter o objeto de estado em sincronia com sua visualização e a representação DOM. Os usuários podem interagir com o aplicativo de várias maneiras e é uma grande tarefa fornecer uma transição limpa de um estado para outro.

Com este tutorial do React.js, saiba mais sobre o gerenciamento de estado de visualização.

Um pouco de história

Não muito tempo atrás, os aplicativos da web tinham um fluxo de dados muito mais simples. O navegador enviaria uma solicitação ao servidor, toda a lógica do aplicativo seria executada no servidor e uma visualização HTML completa seria enviada de volta ao navegador para apresentação ao usuário. As ações subsequentes do usuário (como cliques, envios de formulários etc.) acionariam o mesmo fluxo novamente. Os aplicativos não precisavam se preocupar com o estado do usuário e cada exibição podia ser regenerada enviando uma nova solicitação ao servidor.

No entanto, as aplicações web cresceram em complexidade e as demandas dos usuários da UI/UX também estavam avançando. Recarregar a página inteira, quando apenas uma parte dela muda, era ineficaz e lento. Precisávamos de uma interação rápida, ágil e responsiva com impacto imediato na interface do usuário.

JavaScript veio em socorro. Os desenvolvedores começaram a escrever quantidades significativas de código que eram executados no navegador antes que uma solicitação fosse enviada ao servidor. O jQuery também trouxe avanços significativos para o desenvolvimento web front-end, pois forneceu recursos simples e eficazes, como validação do lado do cliente, janelas modais, mensagens de alerta, animações e até atualizações parciais de página baseadas em Ajax.

Compreendendo a Complexidade

Vamos dar uma olhada em um exemplo simples de avaliar a força de uma senha. Se a senha estiver OK, a caixa de entrada deve ter uma borda verde e deve exibir uma mensagem agradável. Se a senha for fraca, a caixa de entrada deve ter uma borda vermelha e deve exibir uma mensagem de aviso. Também podemos mostrar uma carinha sorridente quando uma senha é suficientemente forte.

O código a seguir demonstra como isso pode ser feito pela manipulação do DOM. Há muitos “ses” aqui, e o código não é muito fácil de ler.

 if (hasInputBorder()) { removeInputBorder(); } if (text.length === 0) { if (hasMessage()) { removeMessage(); } if (hasSmiley()) { removeSmiley(); } } else { var strength = getPasswordStrength(text); if (!hasInputBorder()) { addInputBorder(); } var color = (strength == 'weak' ? 'red' : 'green'); setInputBorderColor(color); var message = (strength == 'weak' ? "Password is weak" : "That's what I call a password!"); if (hasMessage()) { setMessageText(message); } else { addMessageWithText(message); } if (strength == 'weak') { if (hasSmiley()) { removeSmiley(); } } else { if (!hasSmiley()) { addSmiley(); } } }

Como mostrado acima, primeiro precisamos verificar se o usuário forneceu alguma senha e lidar com o caso em que o campo de senha está vazio. E em todos os casos, precisamos garantir que todos os elementos DOM relacionados sejam atualizados corretamente. Isso inclui a mensagem, a borda e o rosto sorridente.

Nosso campo de senha pode estar em um dos três estados: vazio, fraco ou forte. E, conforme observado, temos três elementos DOM diferentes que são afetados pelo estado do campo de senha. Manipular todas as combinações e garantir que nossa visão seja exibida corretamente aumenta a complexidade ciclomática até mesmo para um código simples como este.

O DOM funciona em modo retido , o que significa que ele só lembra o estado atual. Para modificar nossa visão, precisamos fornecer instruções para cada elemento DOM e programar a transição.

A codificação de transições em vez de estados pode ser complexa. O número de ramificações e verificações que precisamos realizar em nosso código cresce exponencialmente com o número de estados de exibição a serem gerenciados.

Em nosso exemplo, definimos três estados de visualização, que nos deram 3 * 2 = 6 transições. Em geral, dados N estados, temos N * (N - 1) = N^2 - N transições que precisaríamos modelar. Basta pensar no aumento da complexidade se adicionarmos um quarto estado ao nosso exemplo.

Normalmente, há muito código relacionado à modelagem das transições . Seria muito melhor se pudéssemos apenas definir nossos estados de visualização e não nos preocuparmos com todos os detalhes da transição de um estado para outro.

Reduzindo a Complexidade

Assumindo que poderíamos declarar o estado da visão com base no estado do modelo, em vez de codificar explicitamente a transição de um estado para outro, poderíamos ter algo assim:

 var strength = getPasswordStrength(text); if (text.length == 0) { return div(input({type: 'password', value: text})); } else if (strength == 'weak') { return div( input({type: 'password', value: text, borderColor: 'red'}), span({}, "Weak") ); } else { return div( input({type: 'password', value: text, borderColor: 'green'}), span({}, "That's what I call a password!"), img({class: 'icon-smiley'}) ); }

Aqui temos três ramos simples de código, representando os três estados possíveis do nosso aplicativo. Apenas retornamos a especificação da view em cada branch, dependendo do estado do modelo. Todo o código de manipulação do DOM é removido; apenas fornecemos as informações sobre o que queremos, e não como chegar lá.

Embora essa abordagem reduza significativamente a complexidade do código, ela também pressupõe que há alguém ou alguma outra coisa para cuidar da manipulação real do DOM em nosso nome.

É aí que entra o React. O React irá garantir que um estado de visualização seja imediatamente gerenciado e atualizado com base no estado do modelo de dados subjacente.

Reação

React é uma biblioteca JavaScript criada pelo Facebook. Ele foi projetado para lidar com a parte da interface do usuário de aplicativos da Web. Você pode pensar nisso como o V na arquitetura MVC. É muito focado. Ele não faz suposições sobre o resto de sua pilha de tecnologia e não lida com nada além de componentes de renderização. Ele não fornece mecanismos de roteamento, modelos ou outros recursos que geralmente são empacotados em estruturas maiores. Assim, você pode misturá-lo e usá-lo com qualquer outra biblioteca ou framework que desejar.

O React nos permite definir UIs como árvores de componentes compostos. Um desenvolvedor React define esses componentes especificando uma função de renderização que descreve o componente, dado o estado de entrada. Essa função deve ser pura (ou seja, não deve ter nenhum efeito colateral ou depender de nada além de sua entrada explícita).

A função render retorna uma descrição da view, que o React chama de Virtual DOM . Pense nisso como um objeto JavaScript correspondente ao elemento DOM renderizado.

Quando você altera o estado do componente, ele apenas renderiza a si mesmo e todos os seus elementos filhos, retornando um novo DOM Virtual .

Além disso, o React não fará uma simples substituição de HTML, ao fazer a transição de um estado para outro. Ele encontrará a diferença entre o estado anterior e o novo e calculará o conjunto mais eficaz de operações DOM para executar uma transição.

Mesmo sem o desempenho em mente, a redução na complexidade do código é significativa e nos permite concentrar nossos esforços nas partes mais exclusivas e complexas de nosso aplicativo.

Para ficar um pouco mais concreto, é assim que nosso exemplo de tutorial seria feito usando React para gerenciar estados de visualização.

NOTA: O exemplo de código a seguir foi escrito no pré-processador JSX, que é uma maneira comum de escrever uma interface do usuário baseada em React.

 function getPasswordStrength(text) { // Some code that calculates the strength given the password text. } var PasswordWithStrength = React.createClass({ getInitialState: function() { return {value: ''}; }, render: function() { var strength = getPasswordStrength(this.state.value); if (this.state.value.length == 0) { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} /> </div>; } else if (strength == 'weak') { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid red'} } /> <span style={{color: 'red'}}>Weak!</span> </div>; } else { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid green'} } /> <span style={{color: 'green'}}>That's what I call a password!</span> <Emoji value="smiley" /> </div>; } }, handleInputChange: function(ev) { this.setState({value: ev.target.value}); } }); React.render(<PasswordWithStrength />, document.body);

O componente Emoji que é renderizado quando a força da senha está OK com <Emoji value="smiley" /> é apenas outro componente personalizado (assim como PasswordWithStrength ). Ele é definido assim:

 var Emoji = React.createClass({ render: function() { var emojiSrc = this.props.value + '.png'; return <img src={emojiSrc}></img>; } });

React.js vs. Outros

Para ser justo, porém, existem outras estruturas JavaScript do lado do cliente (como Ember, Angular, Knockout e outras) que também resolveram o problema de gerenciamento de estado de exibição e até adicionaram mais recursos a ele. Então, por que você quer usar o React em vez de qualquer outro framework?

Existem duas vantagens principais que o React tem, em comparação com a maioria das outras bibliotecas.

Sem vinculação de dados

Algumas das outras estruturas alternativas usam vinculação de dados para mapear elementos DOM para propriedades de estado e mantê-los sincronizados observando as alterações de propriedade. Essa abordagem permite renderizar a visualização uma vez, com cada alteração acionando apenas as modificações dos elementos DOM afetados. Outras alternativas usam verificação suja ; ou seja, em vez de observar as mudanças de propriedades individuais, eles apenas realizam uma diferença entre o estado anterior e o novo. O React é mais parecido com a última abordagem, mas, em vez de comparar estados, compara as representações da visão.

React não tem vinculação de dados. Espera-se que um desenvolvedor chame o método setState ou renderize novamente o componente superior, quando o estado for alterado. Abrange um fluxo unidirecional, do estado para a vista.

Esse conceito é fácil de adotar, pois os desenvolvedores geralmente não pensam em vinculação de dados. O foco está na representação visual dos dados. Portanto, você não precisa pensar em propriedades dependentes, formatação, vinculação de tags HTML especiais, etc. Com o React, você simplesmente re-renderiza o componente quando o modelo muda.

Para entender a diferença no gerenciamento de estado de exibição aqui, vamos comparar Ember e React . Criaremos um objeto person que exibirá o nome completo em letras maiúsculas. Após dois segundos, vamos simular a mudança e atualizar a visualização.

 // EXAMPLE USING EMBER App = Ember.Application.create(); App.Person = Ember.Object.extend({ firstName: null, lastName: null, fullName: function() { return this.get('firstName') + ' ' + this.get('lastName'); }.property('firstName', 'lastName') }); var person = App.Person.create({ firstName: "John", lastName: "Doe" }); Ember.Handlebars.helper('upcase', function(value) { return value.toUpperCase(); }); App.IndexRoute = Ember.Route.extend({ model: function () { return person; } }); setTimeout(function() { person.set('firstName', 'Harry'); }, 2000); // Templates: <script type="text/x-handlebars"> <h2>Welcome to Ember.js</h2> {{outlet}} </script> <script type="text/x-handlebars" data-template-name="index"> The current user is: {{upcase model.fullName}} </script>

Criamos um objeto com as propriedades firstName , lastName e fullName . Como o Ember está observando as mudanças de propriedade, tivemos que especificar que fullName depende do firstName e do lastName . Para fazer isso, adicionamos .property('firstName', 'lastName') quando definimos o fullName .

Após dois segundos, person.set('firstName', 'Harry'); É executado. Isso acionou a atualização da exibição e sua vinculação.

Agora vamos fazer o mesmo em React.

 // EXAMPLE USING REACT var CurrentUser = React.createClass({ render: function() { return <div>The current user is: {this.props.user.fullName().toUpperCase()}</div>; } }); var person = { firstName: 'John', lastName: 'Doe', fullName: function() { return this.firstName + ' ' + this.lastName; } }; var currentUser = React.render(<CurrentUser user={person}/>, document.body); setTimeout(function() { person.firstName = 'Harry'; currentUser.setProps({user: person}); }, 2000);

Mesmo que o código Ember seja simples e fácil de ler, é óbvio que o React ganha em simplicidade. A person é um objeto JavaScript simples, com fullName sendo simplesmente uma função.

Sem modelagem

Cada estrutura alternativa tem uma maneira diferente de lidar com modelos. Alguns deles usam strings que são compiladas em JavaScript, enquanto outros usam elementos DOM diretamente. A maioria deles usa atributos e tags HTML personalizados que são então “compilados” em HTML.

Os modelos não fazem parte do código JavaScript. Por isso, cada alternativa precisa de uma forma personalizada para representar operações comuns, condicionais, iterações, chamadas de funções, etc. Todas elas acabam criando uma nova pseudo-linguagem que os desenvolvedores devem aprender.

Não há templates no React, tudo é simplesmente JavaScript antigo.

O React usa todo o poder do JavaScript para gerar a visualização. O método de renderização do componente é uma função JavaScript.

JSX está disponível como um pré-processador que transforma “sintaxe semelhante a HTML” em JavaScript normal, mas JSX é opcional e você pode usar JavaScript padrão sem nenhum pré-processador. Você também pode aproveitar as ferramentas JavaScript existentes. Linters, pré-processadores, anotações de tipo, minificação, eliminação de código morto, etc.

Vamos usar novamente um exemplo concreto para comparar o React com uma das estruturas alternativas para gerenciamento de estado de exibição.

O tutorial a seguir é um exemplo de uso do AngularJS para listar hashtags e a contagem de tweets para cada uma delas. A lista é classificada por contagem e uma mensagem é exibida se não houver hashtags.

 <!-- EXAMPLE USING ANGULAR --> <div ng-controller="MyCtrl"> <ul ng-show="hashTags.length > 0"> <li ng-repeat="hashTag in hashTags | orderBy:'tweetCount':true"> {{hashTag.name}} - {{hashTag.tweetCount}} tweets </li> </ul> <span ng-show="hashTags.length == 0">No hashtags found!</span> </div>

Para poder fazer esta lista, um desenvolvedor deve aprender sobre AngularJS directives , ng-show e ng-repeat . Então ele precisa aprender sobre AngularJS filters para entender orderBy . Muito trabalho para uma coisa simples como a saída de uma lista!

Agora vamos considerar o exemplo React que faz a mesma coisa:

 // EXAMPLE USING REACT function byTweetCountDesc(h1, h2) { return h2.tweetCount - h1.tweetCount; } //... render: function() { if (this.state.hashTags.length > 0) { var comps = this.state.hashTags.sort(byTweetCountDesc).map(function(hashTag, index) { return <li key={index}> {hashTag.name} - {hashTag.tweetCount} tweets </li>; }); return <ul>{comps}</ul>; } else { return <span>No hashtags found!</span> } }

Mesmo se usássemos a abordagem “mais avançada” e JSX, todo desenvolvedor web com um entendimento básico de JavaScript pode ler facilmente o código acima e entender o que ele faz. Verificação condicional padrão usando if , iteração usando map() , e um 'sort()' padrão vem naturalmente para qualquer desenvolvedor, então não há sintaxe extra ou outros conceitos para aprender.

Conclusão

A principal lição deste tutorial do React.js é o fato de que o React permite que você se concentre no gerenciamento do estado de exibição real em vez de nas transições, simplificando assim seu trabalho e seu aplicativo.

A curva de aprendizado para adotar o React é bastante trivial. Nenhuma linguagem de modelagem personalizada para dominar, nenhuma vinculação de dados para se preocupar e tudo se resume a funções JavaScript que descrevem elementos de interface do usuário.

Para saber mais sobre como simplificar o código de sua aplicação usando React, dê uma olhada nesta palestra de Steven Luscher, Descomplexifying Code with React.

Aqui estão algumas leituras adicionais para quem quiser dar o próximo passo e começar a usar o React:

  • http://jlongster.com/Removing-User-Interface-Complexity,-or-Why-React-is-Awesome