O que você deve saber sobre o funcionamento do React Native

Já faz algum tempo que acompanho diversos artigos e uma coisa que eu sinto falta é saber quem está por trás daquele post. Então, acabo sempre dando scroll até o fim do post e procurando entender um pouco melhor sobre o autor antes mesmo de começar a ler o conteúdo produzido por ele. Então, pensando
O que você deve saber sobre o funcionamento do React Native

Já faz algum tempo que acompanho diversos artigos e uma coisa que
eu sinto falta é saber quem está por trás daquele post. Então, acabo sempre
dando scroll até o fim do post e procurando entender um pouco melhor sobre o
autor antes mesmo de começar a ler o conteúdo produzido por ele.

Então, pensando nisso, aqui vai uma breve introdução sobre mim: meu nome é
Rafael Câmara, sou desenvolvedor frontend apaixonado por design, há cerca
de seis anos e atualmente venho focando meus estudos e trabalhos em React e
React Native.

Durante estes últimos meses de estudos e trabalhos, algumas coisas me
incomodavam profundamente:

  • Como o React Native realmente funciona por baixo dos panos, tirando toda a abstração?
  • Quais são as suas reais diferenças em relação aos seus “concorrentes” como Ionic/Cordova?

Bem que eu poderia ter entrado no código e ter estudado cada uma das funções a
fim de entender o que faz o React Native ser tão especial, mas como todo bom
desenvolvedor, resolvi recorrer ao nosso querido amigo Google. E uma coisa
acabou me surpreendendo… Você encontra diversos posts sobre como iniciar em
React Native ou como resolver uma determinada situação, mas o conteúdo
relacionado ao seu real funcionamento é muito precário; ainda mais na comunidade
brasileira.

Então partindo disso, resolvi juntar tudo o que eu absorvi durante as horas de
talks, posts e estudos que eu tive e escrever aqui; a fim de ajudar aqueles que
estão dando início aos estudos nessa área e até mesmo para abrir discussões
sobre as conclusões que eu cheguei; agregando assim, mais conhecimento para
todos nós.

Como vocês já devem ter percebido, não estou escrevendo este post para quem tem
o intuito de aprender React Native, mas sim para aqueles que desejam entender
como ele funciona; logo você verá pouquíssimo código aqui (Não vá embora
haha).
Para aqueles que desejam realmente aprender React Native, recomendo
fortemente o curso do Tyler McGinnis; ele
vêm fazendo um trabalho extraordinário na comunidade React e React Native.

React Native – Tyler McGinnis

Então vamos começar…

O que o React Native faz? Basicamente, ao contrário de aplicações web, que
se utilizam da DOM do navegador para representar os elementos na UI, ele irá
realizar chamadas nativas — Objective-C para dispositivos iOS e JAVA para
Android
— a fim de renderizar estes elementos.

Entretanto, esta comunicação entre o reino JavaScript (React Native) e o
reino Nativo (Objective-C/Java) não é feita em um passe de mágica. Existe
“algo” ali que não é responsável apenas por realizar toda essa comunicação, mas
sim também pelo diferencial do React Native para outros frameworks e bibliotecas
do mercado… “Algo” que vêm sendo chamado de Bridge.

Muitos desenvolvedores acabam considerando o processo de Bridge como
*black-box *e ignoram o que está acontecendo ali. Eles não estão errados em um
primeiro momento, uma vez que este processo não será parte do seu dia a dia de
desenvolvedor mas poderá ser extremamente útil entender como ele funciona em
determinadas situações.

Mas quando irei precisar da Bridge?

Basicamente você sempre estará usufruindo dos benefícios da Bridge, uma vez
que ela é a chave para o funcionamento de uma aplicação React Native. Mas você
precisará de entender o seu funcionamento em algumas situações como:

  • Integração com third-party SDKs;
  • Quando alta performance é crucial para a sua aplicação;
  • Quando você está trabalhando com aplicativos já existentes (Android/iOS);
  • Você precisa de acessar APIs da plataforma;

Aplicações React Native

Ok, neste ponto você já deve entender que uma aplicação React Native não
funciona apenas com o JavaScript que nós estávamos acostumados, certo? Existe
algo a mais ali naquela black-box.

Para continuar, devemos entender como uma aplicação React Native é estruturada;
de ambos os lados: o lado nativo e o lado do JavaScript que estávamos
acostumados.

É importante fixar que projetos em React Native rodam diretamente no
dispositivo do usuário; nativamente. E para isso, contamos com a seguinte
arquitetura:

Dispositivos Android:

  • JSCore virtual machine: Destinado ao nosso código JavaScript;
  • Android Runtime: Para o código Java;

Dispositivos iOS:

  • JSCore virtual machine: Destinado ao nosso código JavaScript;
  • Native Runtime: Para códigos Objective-C/Swift;

Existem também três importantes threads sendo executadas em uma aplicação
React Native, fora as que normalmente já são executadas por aplicações
nativas em background.

A primeira delas é a main thread, que também é utilizada por qualquer
aplicação nativa. Ela é responsável por tratar as requisições relacionadas a
renderização de elementos na tela e também pelos gestos reproduzidos pelo
usuário.

A segunda é exclusiva ao React Native, responsável por executar o código
JavaScript. O JavaScript é o responsável por toda lógica de negócio da
aplicação. Além de definir a estrutura e as funcionalidades da nossa UI.

E por último, a **Shadow Queue **que é a responsável pelos cálculos referentes
ao layout.

Voltando à analogia que utilizamos anteriormente, podemos considerar que a main
thread e a thread de JavaScript seriam, respectivamente, o reino Nativo e o
reino JavaScript.

Comunicação entre os reinos

Um ponto importante aqui é que essas duas threads — esses dois reinos — nunca
se comunicam diretamente e nunca bloqueiam uma a outra
. Toda essa comunicação
— requisições, medidas de layout, interações com o usuário, etc. — é realizada
através da Bridge; que como o próprio nome diz, é a ponte entre o reino
Nativo e o reino JavaScript. Toda a comunicação entre estes dois reinos deve
passar por essa ponte.

Todo início de interação é iniciado pelo lado nativo, já que eventos de toque na
tela são tratados no parte nativa da aplicação.

Para exemplificar um pouco melhor e entender a responsabilidade de cada uma das
threads, imagine a seguinte situação:

1. [Nativo]: Opa JavaScript, tudo bom? O usuário acabou de clicar aqui no
botão MOSTRAR FILMES. O que eu preciso fazer?

2. [JavaScript]: Fala Nativo! Não tem nenhum filme na lista do usuário. Você
deve renderizar o seguinte texto para ele “Nenhum filme encontrado!”.

3. [Nativo]: Feito, está renderizado! Estou te enviando a instância do
elemento que eu acabei de criar.

Toda essa comunicação é feita de forma serializada e agrupada.

Mas como as coisas não são tão simples assim no mundo de desenvolvimento, esta
comunicação seria algo que seguisse o seguinte padrão.

Nós podemos ver que esta linha está se referindo a uma comunicação JS->N, ou
seja, a nossa thread de JavaScript está solicitando que a thread Nativa
realize alguma ação. (Esta comunicação poderia também ser N->JS)

Estamos passando um método de UIManager chamado createView, a qual
recebe um array como parâmetro que possui as propriedades de uma View que deve
ser criada.

UIManager é o responsável por aplicar comandos do React na plataforma nativa

Esta view que deverá ser criada é elemento chamado RCTRawText, que possui
como propriedade text: “Nenhum filme encontrado!”.

Mas espere um momento, o que seria este RCTRawText?

Para aqueles que não sabem, o React e React Native nos possibilita utilizar
JSX para representar os nossos elementos, de uma forma similar ao XML; não é
obrigatório mas é altamente recomendado por ser de simples leitura,
interpretação e por possibilitar a utilização de todo o poder do JavaScript.

Github do JSX

Mas infelizmente o nosso reino nativo não sabe como interpretar esse JSX.
Logo para resolver isto, o React Native transforma o nosso elemento JSX para uma
linguagem de marcação que pode ser entendida pelo nosso processamento nativo.
Esta linguagem de marcação irá determinar como o elemento de nossa UI deverá
ser.

Como nós podemos perceber a partir do gist a cima, o nosso JSX foi segmentado
em diversos novos elementos que serão utilizados para renderizar o nosso
elemento.

Vale notar que estes elementos da linguagem de marcação não serão renderizados
diretamente na nossa UI. Eles irão apenas auxiliar neste processo, de forma que
o seu elemento de texto *(de acordo com este exemplo) *seja renderizado
nativamente.

MessageQueue.js

Todas essas mensagens JS->N e *N->JS *são recebidas e tratadas pelo o que
chamamos de MessageQueue, que possui uma parte nativa e uma correspondente
JavaScript, com o objetivo de realizar o mapeamento entre estes dois mundos.

Quando nós estamos pensando em performance, nós temos que nos preocupar bastante
com o lado JavaScript da MessageQueue. Uma vez que nós temos que ter certeza
que a nossa thread JavaScript não está bloqueda e que o MessageQueue não está
congestionado; uma vez que o seu processamento é síncrono.

Caso você já tenha desenvolvido em React Native antes, você muito provavelmente
já se deparou com o sentimento de conseguir dar scroll na tela do seu aplicativo
mas não conseguir clicar em nenhum botão. Isso acontece porque a sua UI é feita
inteiramente de forma nativa, enquanto o JavaScript é o responsável por saber o
que deve ser feito quando um toque é realizado. Neste momento, muito
provavelmente a sua MessageQueue estava congestionada e os seus eventos de
toque na tela ainda estavam “esperando” para serem transmitidos.

Existem algumas boas práticas que devem ser seguidas caso você queira evitar
problemas com a MessageQueue, entre eles, os seguintes:

  • Evite objetos “nested”. Procure sempre deixar os seus objetos o mais planos
    possível;
  • Evite passar arrays pela bridge;
  • Escreva a sua lógica de negócio em apenas do lado JavaScript;

A MessageQueue possui um recurso chamado spy (espião) que nós podemos
habilitar que nos permite possuir os logs de todas as mensagens passadas do
contexto JavaScript para o Nativo. Caso você tenha o interesse de ativar o
mesmo, basta você utilizar o método *spy *passando como parâmetro o valor
booleano true.

Existe uma segunda alternativa para se visualizar os logs passados pela **Bridge
**que se chama rn-snoopy. Ele também utiliza o método *spy *da MessageQueue.

Na verdade, o rn-snoopy utiliza RxJS e trata os dados da MessageQueue como
stream; o que nos permite filtrar os dados e exibir aquilo que realmente é
pertinente para a nossa situação.

jondot/rn-snoopy

Revisando: Aplicação RN e Comunicação

Então, revisando um pouco sobre o que foi falado (escrito):

  • Em uma aplicação React Native temos dois reinos: Nativo e JavaScript;
  • Estes reinos se comunicam apenas através de uma Bridge;
  • A Bridge possui as seguintes características: assíncrona, serializada e
    agrupada;
  • Apesar da comunicação com a Bridge ser assíncrona, a MessageQueue é
    síncrona. Logo, ela interpreta as mensagens na ordem de chegada;
  • Uma aplicação React Native é realmente nativa, uma vez que seus componentes
    são renderizados nativamente;
  • Podemos visualizar os logs da Bridge utilizando .spy() ou o rn-snoopy;

Native Modules e UI Components

Neste ponto você já deve ter uma boa noção do real funcionamento de uma
aplicação React Native e de como acontece a comunicação entre o lado nativo e o
lado JavaScript dessa aplicação.

No entanto, início deste texto, eu disse que você provavelmente iria precisar de
conhecer o funcionamento da Bridge nas seguintes situações:

  • Integração com third-party SDKs;
  • Quando alta performance é crucial para a sua aplicação;
  • Quando você está trabalhando com aplicativos já existentes (Android/iOS);
  • Você precisa de acessar APIs da plataforma;

E durante o texto eu não expliquei como você poderia reaproveitar algum método
ou componente que você já possui em sua aplicação Android/iOS. Isto é feito
através de Native Modules e UI Components, respectivamente.

Entretanto, nós iremos deixar esses dois tópicos para um post futuro. Caso você
tenha interesse, me siga no Twitter e fique sabendo assim que eu publicar. :)

Últimas Considerações

Espero que vocês tenham aproveitado o conteúdo deste post assim como eu
aproveitei ao escrever :)

O meu objetivo aqui foi tentar passar um pouco do o que eu aprendi nestes
últimos meses de trabalho. Fico à disposição caso alguém tenha alguma dúvida ou
sugestão em relação ao conteúdo passado aqui.

Segue alguns links, caso vocês queiram entrar em contato comigo:

Muito obrigado por ter chegado até aqui! :)

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *