Um mergulho frio no React Native (Tutorial para iniciantes)

Publicados: 2022-03-11

Quando o React Native foi anunciado, as primeiras reações foram extremamente positivas. Tradicionalmente, quando pensamos em tecnologias da web no espaço móvel, coisas como Apache Cordova vêm à mente, que nos permite empacotar sites ou aplicativos da web como aplicativos para plataformas móveis. Neste tutorial para iniciantes, veremos a arquitetura do React Native, a filosofia por trás do React Native e como ele difere de outras soluções no mesmo espaço. Ao final do artigo, teremos transformado uma aplicação React “Hello World” em uma aplicação React Native.

Vamos começar dizendo que o React Native é uma tecnologia relativamente nova. Ele está disponível oficialmente desde março de 2015, estando em beta privado desde o início daquele ano, e usado internamente no Facebook por um tempo antes disso. O ditado “Roma não foi construída em um dia” geralmente também se aplica à tecnologia. Ferramentas como grunt e plataformas como Node.js levaram anos para amadurecer. No mundo da web, as coisas estão se movendo rapidamente, e com um grande número de frameworks, pacotes e ferramentas sendo lançados todos os dias, os desenvolvedores tendem a ficar um pouco mais céticos e não querem pular em cada onda de hype apenas para perceber que eles acabaram em uma situação de aprisionamento de fornecedor. Veremos o que torna o React Native especial, por que é uma tecnologia que vale a pena entrar e abordaremos alguns casos em que nem tudo são unicórnios e arco-íris.

Sob o capô

Ao falar sobre tecnologias da web em dispositivos móveis, as soluções disponíveis geralmente se enquadram em uma das seguintes categorias.

Agrupando aplicativos da Web em um navegador da Web móvel

O aplicativo da Web fica em um navegador móvel, normalmente chamado de WebView. Sem qualquer refatoração importante, um site ou aplicativo da web funciona no dispositivo móvel. Talvez seja necessário considerar os eventos do navegador móvel, como tocar ou ouvir as mudanças de orientação do dispositivo e a tela menor para uma experiência completa do usuário, mas temos uma versão móvel funcional com o mínimo de esforço. Cordova/PhoneGap é a opção mais popular nesta categoria. Infelizmente, esta opção tem uma grande desvantagem: em alguns casos, os aplicativos desenvolvidos usando o Cordova são significativamente mais lentos que os aplicativos nativos, especialmente para aplicativos com gráficos pesados. Em outros casos, o sistema operacional móvel não fornece todos os recursos do WebView que estão disponíveis no navegador móvel. A experiência do usuário também pode ser diferente dos aplicativos nativos; isso pode acontecer devido ao aplicativo ou a própria plataforma. Esse problema pode variar de barras de rolagem que não têm a mesma sensação até um atraso perceptível ao tocar nos elementos.

Compilando para tecnologias nativas

Uma solução completamente diferente é criar uma base de código nativa no final. Isso acontece transformando o código-fonte original em outra linguagem de programação. Trocamos o desempenho nativo por uma camada de abstração com algumas incertezas. Em casos de soluções de código fechado, não temos certeza do que acontece nos bastidores e com que tipo de caixa preta estamos lidando. Em outros casos, não temos certeza de quanto a próxima atualização do sistema operacional móvel quebrará nosso código e quando as correções ou atualizações estarão disponíveis. Um exemplo popular desta categoria seria Haxe.

Usando uma camada JavaScript

Aqui, usamos o mecanismo JavaScript do ambiente móvel e executamos nosso JavaScript lá. Os controles nativos são mapeados para objetos e funções JavaScript, portanto, quando chamávamos uma função chamada fancyButtonRightHere() , um botão aparecia na tela. NativeScript ou Appcelerator Titanium são exemplos bem conhecidos desta categoria.

O React Native pode ser classificado como algo da terceira categoria. Para as versões iOS e Android, o React Native usa JavaScriptCore sob o capô, que é o mecanismo JavaScript padrão no iOS. JavaScriptCore também é o mecanismo JavaScript nos navegadores Safari da Apple. Os desenvolvedores do OS X e iOS podem interagir diretamente com ele, se quiserem.

Uma grande diferença é que o React Native executa o código JavaScript em um thread separado, então a interface do usuário não bloqueia e as animações devem ser suaves e suaves.

Reagir é o principal recurso

Vale a pena notar que o “React” no React Native não é colocado lá por acaso. Para o React Native, precisamos entender exatamente o que o React oferece. Os conceitos a seguir funcionam da mesma forma no React e no React Native, embora esses exemplos de código sejam adaptados para serem executados no navegador.

Ponto de entrada de renderização único

Quando damos uma olhada em um componente React simples, a primeira coisa que podemos notar é que o componente tem uma função de render . Na verdade, o React gera um erro se não houver nenhuma função de renderização definida dentro do componente.

 var MyComponent = function() { this.render = function() { // Render something here }; };

O especial é que não mexemos com elementos DOM aqui, mas retornamos uma construção baseada em XML que representa o que será renderizado no DOM. Essa construção baseada em XML é chamada JSX.

 var MyComponent = function() { this.render = function() { return <div className="my-component">Hello there</div>; }; };

Um transformador JSX especial pega todo esse código de aparência XML e o converte em funções. Esta é a aparência do componente após a transformação:

 var MyComponent = function() { this.render = function() { return React.createElement("div", { className: "my-component" }, "Hello there"); }; };

A maior vantagem é que, ao dar uma olhada rápida no componente, sempre sabemos o que ele deve fazer. Por exemplo, um componente <FriendList /> pode renderizar vários componentes <Friend /> . Não podemos renderizar nossos componentes em nenhum outro lugar que não seja dentro da função de render , então nunca há a preocupação de não sabermos exatamente de onde veio nosso componente renderizado.

Fluxo de dados unidirecional

Para construir o conteúdo de um componente, o React fornece propriedades ou props para abreviar. Semelhante aos atributos XML, passamos as props diretamente para um componente e podemos usar as props dentro do componente construído.

 var Hello = function(props) { this.render = function() { return <div className="my-component">Hello {props.name}</div>; }; }; var Greeter = function() { this.render = function() { return <Hello name="there" /> } };

Isso faz com que nossos componentes fiquem em uma estrutura semelhante a uma árvore, e só podemos passar dados ao construir elementos filhos.

Re-renderizar nas alterações

Além dos adereços, os componentes também podem ter um estado interno. O exemplo mais proeminente desse comportamento seria um contador de cliques que atualiza seu valor quando um botão é pressionado. O número de cliques em si seria salvo no estado.

Cada mudança de prop e de estado aciona uma nova renderização completa do componente.

DOM virtual

Agora, quando tudo é re-renderizado quando os adereços ou o estado mudam, como é que o próprio React está funcionando tão bem? O ingrediente mágico é o “DOM Virtual”. Sempre que algo precisa ser renderizado novamente, uma representação virtual do DOM atualizado é gerada. O DOM Virtual consiste em representações leves de elementos modelados a partir da árvore de componentes, tornando o processo de gerá-los muito mais eficiente do que gerar elementos DOM reais. Antes de aplicar as alterações ao DOM real, são feitas verificações para determinar onde exatamente na árvore de componentes as alterações ocorreram, um diff é criado e apenas essas alterações específicas são aplicadas.

Começando com este tutorial do React Native

Existem certos pré-requisitos que os iniciantes precisarão configurar para desenvolver para o React Native. Como o iOS foi a primeira plataforma suportada e a que estamos abordando neste tutorial, precisamos do macOS e do Xcode, pelo menos a versão 6.3. Node.js também é necessário. O que ajuda é instalar o Watchman através do gerenciador de pacotes Brew com brew install watchman . Embora isso não seja necessariamente necessário, ajuda ao lidar com muitos arquivos dentro do nosso projeto React Native.

React Native: A estrutura de desenvolvimento de aplicativos móveis mais segura.
Tweet

Para instalar o React Native, basta instalar o aplicativo de linha de comando React Native com npm install -g react-native-cli . Chamar o comando react-native nos ajuda a criar um novo aplicativo React Native. A execução react-native init HelloWorld cria uma pasta chamada HelloWorld na qual o código padrão pode ser encontrado.

Animação do terminal mostrando como configurar um aplicativo React Native "Hello World".

Transformando um aplicativo React

Com o React sendo o recurso principal e os princípios básicos vindos da biblioteca React, vamos dar uma olhada no que precisamos para transformar um aplicativo React “Hello World” mínimo em um React Native.

Usamos alguns recursos do ES2015 neste exemplo de código, especificamente classes. É completamente viável ficar com React.createClass ou usar um formulário de função semelhante ao padrão de módulo popular.

 var React = require('react'); class HelloThere extends React.Component { clickMe() { alert('Hi!'); } render() { return ( <div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

Etapa 1: abrace os módulos CommonJS

Na primeira etapa, precisamos mudar exigindo que o módulo React use react-native .

 var React = require('react-native'); class HelloThere extends React.Component { clickMe() { alert('Hi!'); } render() { return ( <div className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</div> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

O que geralmente faz parte do pipeline de ferramentas ao desenvolver um aplicativo Web React é parte integrante do React Native.

Etapa 2: não há DOM

Não surpreendentemente, não há DOM no celular. Onde anteriormente usamos <div /> , precisamos usar <View /> e onde usamos <span /> , o componente que precisamos aqui é <Text /> .

 import React from 'react'; import {View, Text, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert('hi!'); } render() { return ( <View className="box" onClick={this.clickMe.bind(this)}>Hello {this.props.name}. Please click me.</View> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

Embora seja bastante conveniente colocar texto diretamente em elementos <div /> , no mundo nativo o texto não pode ser colocado diretamente em um <View /> . Para isso precisamos inserir um componente <Text /> .

 import React from 'react'; import {View, Text, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert('hi!'); } render() { return ( <View className="box" onClick={this.clickMe.bind(this)}> <Text>Hello {this.props.name}. Please click me.</Text> </View> ); } } React.render(<HelloThere name="Component" />, document.getElementById('content'));

Etapa 3: os estilos inline são o caminho a percorrer

O React Native nos permite usar a modelagem Flexbox em vez de mexer com float e inline-block quais estamos tão familiarizados no mundo da web. O interessante é que o React Native não usa CSS.

 import React from 'react'; import {View, Text, StyleSheet, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert('hi!'); } render() { return ( <View style={styles.box} onClick={this.clickMe.bind(this)}> <Text>Hello {this.props.name}. Please click me.</Text> </View> ); } } var styles = StyleSheet.create({ box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 } }); React.render(<HelloThere name="Component" />, document.getElementById('content'));

Usar estilos inline parece confuso para iniciantes. É semelhante à transição pela qual os desenvolvedores do React tiveram que passar ao serem confrontados com o JSX e anteriormente usarem mecanismos de modelagem como Handlebars ou Jade.

A ideia é que não tenhamos folhas de estilo globalmente na forma como usamos CSS. Declaramos as folhas de estilo diretamente no nível do componente e, portanto, temos todas as informações necessárias para ver o que nosso componente faz, o layout que cria e os estilos que aplica.

 import React from 'react'; import {Text} from 'react-native'; var Headline = function(props) { this.render = () => <Text style={headlineStyle.text}>{props.caption}</Text>; }; var headlineStyles = StyleSheet.create({ text: { fontSize: 32, fontWeight: 'bold' } }); module.exports = Headline;

Etapa 4: lidar com eventos

O equivalente a clicar em páginas da web é tocar em um elemento no dispositivo móvel. Vamos alterar nosso código para que o “alerta” apareça quando tocamos no elemento.

 import React from 'react'; import {View, Text, StyleSheet, TouchableOpacity, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert("Hi!") } render() { return ( <TouchableOpacity onPress={this.clickMe()}> <View style={styles.box}> <Text>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> ); } } var styles = StyleSheet.create({ box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 } }); React.render(<HelloThere name="Component" />, document.getElementById('content'));

Em vez de os eventos estarem disponíveis diretamente nos componentes <View /> , precisamos usar explicitamente elementos que acionam eventos, no nosso caso um evento de toque ao pressionar a visualização. Existem diferentes tipos de componentes táteis disponíveis, cada um deles fornecendo um feedback visual diferente.

Etapa 5: personalizar o comportamento entre plataformas

É possível detectar em qual plataforma o aplicativo React Native está sendo executado, acessando o valor de Platform.OS . Digamos que, no exemplo acima, queríamos exibir uma mensagem de alerta diferente com base na plataforma em que estamos executando. Podemos fazer assim:

 ... clickMe() { var message = ''; if(Platform.OS == 'ios') { message = 'Welcome to iOS!'; } else if(Platform.OS == 'android') { message = 'Welcome to Android!'; } Alert.alert(message); } ...

Alternativamente, o método select também está disponível, que fornece uma sintaxe semelhante a um switch:

 … clickMe() { Alert.alert(Platform.select({ ios: 'Welcome to iOS!', android: 'Welcome to Android!' }) ); } ...

Etapa 6: fontes personalizadas e react-native link

Para adicionar uma fonte personalizada, precisamos pular alguns aros. Antes de tudo, certifique-se de que o nome completo da fonte e o nome do arquivo da fonte sejam os mesmos: o iOS usará o nome completo da fonte para pegá-la, enquanto o Android usará o nome do arquivo.

Portanto, se o nome completo da sua fonte for myCustomFont , certifique-se de que o nome do arquivo da fonte seja myCustomFont.ttf .

Depois disso, precisamos criar uma pasta de ativos e apontar o npm para ela. Podemos fazer isso criando a pasta primeiro, em assets/fonts no diretório raiz do aplicativo. Qualquer outro diretório servirá, mas este é o nome convencional usado para o diretório de fontes.

Podemos dizer ao npm onde temos nossos ativos adicionando uma propriedade Assets na seção de integração npm do React, rnpm:

 "rnpm": { "Assets": [ "./assets/fonts/" ] }

Depois de fazer tudo isso, podemos finalmente executar react-native link . Isso copiará as fontes para os diretórios corretos e adicionará o xml necessário ao info.plist no iOS.

Uma vez feito, podemos usar nossa fonte apenas referenciando-a em qualquer folha de estilo por seu nome completo. Vamos usá-lo em nosso elemento Text :

 import React from 'react'; import {View, Text, StyleSheet, TouchableOpacity, Alert} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert("Hi!") } render() { return ( <TouchableOpacity onPress={this.clickMe()}> <View style={styles.box}> <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> ); } } var styles = StyleSheet.create({ box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 }, message: { fontFamily: 'myCustomFont' } }); React.render(<HelloThere name="Component" />, document.getElementById('content'));

Passo 7: Movendo as coisas

O React Native usa as mesmas regras do Flexbox para o layout dos componentes. Digamos que queiramos posicionar nosso botão na parte inferior da tela: vamos envolver nosso TouchableOpacity com um contêiner View :

 <View style={styles.container}> <TouchableOpacity onPress={this.clickMe.bind(this)}> <View style={styles.box}> <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> </View>

E agora vamos definir o estilo do container , junto com os outros estilos já definidos:

 container: { flex: 1, justifyContent: 'center', alignItems: 'center' }

Vamos nos concentrar em justifyContent e alignItems . Essas duas propriedades controlam como o componente é alinhado respectivamente ao longo de seu eixo primário e de seu eixo secundário. Por padrão, o eixo primário é o vertical e o eixo secundário é o eixo horizontal (você pode alterar isso definindo a propriedade flexDirection como row ).

justifyContent tem seis valores possíveis para os quais pode ser definido:

  • flex-start posicionará todos os elementos juntos, no início da caixa delimitadora do componente.
  • flex-end irá posicionar todos os elementos no final.
  • center posicionará todos os elementos no centro da caixa delimitadora.
  • space-around espalhará os componentes uniformemente e centralizará os componentes em suas caixas delimitadoras criadas.
  • space-evenly também espalhará os componentes uniformemente, mas tentará deixar uma quantidade igual de espaço entre os componentes e os outros limites.
  • space-between espalhará os componentes mantendo o espaçamento entre os componentes adjacentes igual.

alignItems pode ser definido com quatro valores possíveis: flex-start , flex-end , center e stretch . Os três primeiros se comportam como para justifyContent , enquanto stretch definirá o componente para ocupar todo o espaço disponível ao longo do eixo, de modo que o eixo seja completamente preenchido.

Então, como queremos que nosso TouchableOpacity seja exibido na parte inferior e centralizado ao longo do eixo horizontal, podemos alterar o estilo da seguinte forma:

 container: { flex: 1, justifyContent: 'flex-end', alignItems: 'center' }

Mais informações sobre os valores que justifyContent e alignItems podem ter podem ser encontradas aqui e aqui.

Etapa 8: registrando o aplicativo

Ao desenvolver com React para o navegador, precisamos apenas definir um ponto de montagem, chamar React.render e deixar o React fazer sua mágica. No React Native, isso é um pouco diferente.

 import React from 'react'; import {View, Text, StyleSheet, TouchableOpacity, Alert, Platform} from 'react-native'; class HelloThere extends React.Component { clickMe() { Alert.alert(Platform.select({ ios: 'Welcome to iOS!', android: 'Welcome to Android!' })); } render() { return ( <View style={styles.container}> <TouchableOpacity onPress={this.clickMe.bind(this)}> <View style={styles.box}> <Text style={styles.message}>Hello {this.props.name}. Please click me.</Text> </View> </TouchableOpacity> </View> ); } } var styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'flex-start', alignItems: 'center' }, box: { borderColor: 'red', backgroundColor: '#fff', borderWidth: 1, padding: 10, width: 100, height: 100 }, message: { fontFamily: 'myCustomFont' } }); var MainComponent = function() { this.render = function() { return <HelloThere name="Component" />; } }; AppRegistry.registerComponent('MainComponent', function() { return MainComponent; });

Temos que registrar o componente para o lado Objective-C das coisas, o que é feito usando o objeto AppRegistry . O nome que damos deve corresponder ao nome dentro do projeto Xcode.

Nosso aplicativo Hello World React Native tem significativamente mais linhas de código do que sua contraparte da web, mas, por outro lado, o React Native leva a separação de interesses um pouco mais longe, especialmente porque os estilos são definidos com o componente.

Como observação lateral, não devemos religar o método clickMe ao contexto this no método de render , especialmente se nosso aplicativo React (Nativo) se tornar um pouco mais complexo. Ele religa o método em cada chamada de renderização, o que pode se tornar bastante. A alternativa é vincular o método dentro do construtor.

Executando o Aplicativo

Para executar o aplicativo, precisamos substituir o conteúdo do arquivo index.ios.js pelo trecho de código do nosso aplicativo transformado da última etapa. Então só precisamos abrir o projeto Xcode e pressionar o grande botão Executar. Primeiro, um terminal será aberto com o servidor React Native e, em seguida, a janela do simulador aparecerá. O servidor React Native cria um pacote, que o aplicativo nativo buscará. Isso permite um ciclo de desenvolvimento rápido semelhante ao desenvolvimento web, onde as alterações serão refletidas quase instantaneamente no simulador.

Para Android, basta adicionar o seguinte ao seu arquivo package.json , em scripts :

 "android-linux": "react-native bundle --platform android --dev false --entry-file index.ios.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/ main/res && react-native run-android"

E então execute npm run android-linux . Certifique-se de que o diretório android/app/src/main/assets exista de antemão.

Depois que o terminal aparecer, nosso aplicativo aparecerá no simulador. Pressionar CMD+D mostrará um menu de desenvolvimento. Clicar na caixa mostrará um alerta. A versão do iOS:

Um telefone da Apple com um pop-up de alerta dizendo "Oi".

E o Android renderiza algo assim:

Um telefone Android com um pop-up de alerta dizendo "Oi".

Para distribuição, ter um aplicativo que aponta para um servidor de desenvolvimento local não funcionaria para nós. Por esse motivo, podemos criar o pacote para uso quando o servidor React Native não estiver em execução com o comando react-native bundle . Nesse caso, precisamos atualizar o método didFinishLaunchingWithOptions do AppDelegate para usar o pacote offline.

Este aplicativo de exemplo também está disponível no Github.

Trabalhando com React Native

Outra coisa que vale a pena mencionar é que não usamos apenas conceitos de React e JavaScript para nossos aplicativos móveis, mas alguns dos fluxos de trabalho que os desenvolvedores da Web estão acostumados também estão disponíveis com React Native. Ao vir do desenvolvimento web, estamos acostumados a ferramentas de desenvolvimento, inspecionando elementos e recarregando ao vivo.

A maneira como o React Native funciona é que ele coloca todos os nossos arquivos JavaScript em um pacote. Esse pacote é servido a partir de um servidor ou empacotado junto com o aplicativo. O primeiro é incrivelmente útil para desenvolvimento no Simulador, pois podemos habilitar o recarregamento ao vivo. O menu do desenvolvedor que o React fornece não é tão poderoso quanto o Chrome Developer Tools, mas fornece uma experiência de desenvolvedor muito parecida com a da Web com recarga e depuração ao vivo com as ferramentas de desenvolvedor/depurador do Chrome (ou Safari).

Os desenvolvedores da Web estão familiarizados com JSFiddle ou JSBin, um playground online para testes rápidos da Web. Existe um ambiente semelhante que nos permite experimentar o React Native em um navegador da web.

Reagir nativo: uma escolha sólida e moderna

Eu havia sugerido originalmente uma abordagem mais cautelosa para o React Native. Hoje, é uma escolha madura e sólida.

Uma das grandes vantagens do React é que ele não se impõe ao seu fluxo de trabalho, pois apenas representa a camada de visualização. Deseja definir seu próprio pipeline do Grunt? Ou você prefere usar o Webpack? E você usará o Backbone.js para suas necessidades de modelo? Ou você quer ir com objetos JavaScript simples? As respostas para todas essas perguntas dependem totalmente de você, pois o React não impõe nenhuma restrição a essas escolhas. Como o site oficial disse: “Como o React não faz suposições sobre o restante de sua pilha de tecnologia, é fácil experimentá-lo em um pequeno recurso em um projeto existente”.

Até certo ponto, isso também é verdade para o React Native. Os desenvolvedores móveis podem integrar o React Native como parte de seu aplicativo, aproveitar o fluxo de trabalho de desenvolvimento inspirado na Web e optar por integrar a biblioteca em uma escala maior, se necessário.

De qualquer forma, uma coisa é certa: o React Native não vai desaparecer. O Facebook tem uma grande participação em ter vários aplicativos com tecnologia React Native nas lojas de aplicativos. A comunidade em torno do React Native é enorme e continua a crescer.

Relacionado: Construir um QR Scanner: um tutorial de câmera React Native