Planejando uma aplicação Offline First no React Native

Falaaaa galera, tudo certo com vocês? Espero que sim =)

Antes de começarmos recomendo a leitura desse post top do Diego no Blog, que vai te dar uma introdução ao Offline First pra você ficar mais situado do que veremos aqui 😉

De forma resumida, podemos entender offline-first como:

Offline first é a arquitetura de uma aplicação mobile com funcionamento semelhante independente da conexão de internet. Para aplicar essa técnica no React Native precisamos utilizar algumas ferramentas e bibliotecas.

Pontos a se considerar no Offline First

Antes mesmo de passar a lista de pontos que devem ser considerados para essa implementação, vou explicar a motivação dessa explicação, enquanto projetava um curso abordando na prática a implementação de um aplicativo seguindo a arquitetura Offline First foi necessário parar e pensar o que deveria ser considerado e decidido antes de começar com a implementação.

E a necessidade do planejamento é até para conseguir decidir quais ferramentas utilizar, e por isso resolvi repassar isso para que os ajude na aplicação dessa arquitetura.

Agora sim, seguem os pontos que levantei para serem analisados:

  • Banco de Dados
  • Redux
  • Quais dados persistir?
  • Ordem das Operações
  • Multi Usuários
  • Interface Otimista

Banco de Dados

Os dados, mesmo quando a aplicação tiver conexão com a internet, devem ser armazenados em algum local, e esse local é o Banco de Dados, no React Native não há uma gama de opções assim como em uma aplicação Back-end.

O React Native oferece um banco de dados padrão para ser usado, o Async Storage, porém, por ele armazenar apenas Strings não há como realizar querys mais complexar envolvendo filtros, mas ele não deixa de ser uma boa opção quando usado para persistir os dados dos Reducers (veremos na sessão do Redux).

// Recuperação de Registros com o Async Storage

try {
  const value = await AsyncStorage.getItem('TASKS');
  if (value !== null) {
    // We have data!!
    console.log(value);
  }
 } catch (error) {
   // Error retrieving data
 }

-

Em contrapartida temos o RealmDB que se trata de um banco mais robusto capaz de armazenar algumas estruturas de dados, permitindo assim a realização de querys para obtenção de dados relacionados.

// Exemplo de uso do RealmDB

let realm = new Realm({ schema: [Person] });

realm.addListener((sender, name) => {
  if (name === 'change') {
    this.setState({ source: sender.objects('Dog') });
  }
});

Mesmo analisando as vantagens e desvantagens de ambos os bancos citados acima não tem como dizer para usar um ou outro, pois essa decisão depende da arquitetura e dados da aplicação.

Redux

O Redux é um Gerenciador de Estados que pode ser usado em aplicações React, ele permite que os dados da aplicação sejam centralizados em um Store, que pode ser acessado de qualquer componente.

Na arquitetura da aplicação, quando o Redux é usado, colabora com o Offline First no sentido de que os dados podem ser mais facilmente persistidos e recuperados para serem usados na aplicação, isso porque você não tem que se preocupar em recuperar os dados de cada componente, eles estarão todos em um único lugar e acessíveis de qualquer lugar.

Falando em Redux, há uma lib que incrementa uma funcionalidade incrível na sua aplicação, estou falando do Redux Persist, que quando adicionado à uma aplicação ele garante a persistência automática dos dados presentes nos Reducers no Async Storage, essa funcionalidade é um grande passo para implementação do Offline First.

// Redux + Redux Persist

const App = () => (
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <Routes />
    </PersistGate>
  </Provider>
);

Recomendo fortemente o uso de Redux + Redux Persist quando você for fazer algo pensando em Offline First.

Quais dados persistir?

Esse é um ponto interessante, pois levando em conta o que eu disse acima dá a perceber que devemos salvar todo tipo de dado do usuário, porém isso não é verdade, temos que analisar muito bem quais os dados que devem ser persistidos.

Quando salvamos todos os dados podemos estar tomando uma decisão errada, pois isso pode prejudicar, e muito, a performance da sua aplicação, isso porque você apenas estaria adicionando mais dados para serem carregados na inicialização da aplicação.

O ideal é que você persista os dados que o usuário possa visualizar, que gere uma boa experiência para o usuário o usar a aplicação, por exemplo, no Facebook quando não há conexão com a internet você ainda assim consegue visualizar uma certa quantidade de posts na sua Timeline, porém não consegue acessar grupos ou páginas que você curtiu.

Ordem das Operações

O mal gerenciamento da execução das ações a aplicação pode causar um problemão para seu back-end ou serviço que a aplicação esteja consumindo, pois é preciso garantir que mesmo offline a ordem que as ações sejam executadas de modo síncrono para não gerar inconsistência de dados.

E para isso temos 2 opções, que como o Banco de Dados, depende da aplicação, a primeira opção é o próprio RealmDB, que já vem com algumas funcionalidades de sincronismo de dados.

Mas caso não esteja usando esse banco de dados, uma lib muito boa para garantir a ordem de execução das operações é o Redux Offline, ele permite você adicionar alguns metadados nas ações disparadas que quando não há conexão ele guarda isso em uma fila, e quando a conexão volta, ele executa na ordem sem a perda de dados.

// Estrutura da Action para entrar na Fila de Ações Offline

type ActionToBeQueued = {
  type: string,
  payload?: any,
  meta: {
    retry?: boolean, // By passing true, your action will be enqueued on offline mode
    dismiss?: Array<string> // Array of actions which, once dispatched, will trigger a dismissal from the queue
  }
}

Multi Usuários

Em qualquer aplicação haverá múltiplos usuários, e isso é algo que se não planejado, pode causar problemas em uma arquitetura Offline First.

Isso porque quando há conexão é fácil executar as ações do Redux ou até mesmo enviar e receber dados de uma API, ou se um serviço, mas quando não houver conexão é preciso adotar alguma técnica para identificar qual usuário está autenticado.

Para que no momento que a conexão seja retomada o sincronismo não aconteça com os dados de outro usuário, ou um usuário executar as ações e no momento do sincronismo não conseguir executar as ações pois não há identificação alguma do usuário que está logado.

Uma das táticas que você pode adotar ao pensar nesse ponto é associar os dados salvos no Banco de Dados Offline (RealmDB, Async Storage, SQLite, etc…) com o ID do usuário logado no momento, um exemplo de uma Action que poderia ser usada:

{
  type: 'ADD_REQUEST',
  payload: {
    id: 1
    data: { username: 'csorlandi', email: 'claudio@rocketseat.com.br', password: '123456' },
  },
  meta: {
    retry: true
  }
}

Assim você salva esse Id no Reducer e sua action estará com os dados do usuário no momento em que o sincronismo for realizado.

Interface Otimista

E por último, mas não menos importante, temos que pensar muito bem na Interface da aplicação, pois muitas vezes desenvolvemos uma interface sem pensar como ela afetará na usabilidade da aplicação, é ai que entra a Interface Otimista.

Interface Otimista é você não esperar uma ação terminar de ser executada para mostrar o resultado.

O conceito por trás desse nome é bem simples, e ainda sim sua aplicação tem um grande impacto na felicidade do usuário (ou para os fãs da Apple, na Satisfação do cliente).

E isso acontece em primeiro lugar porque sua aplicação irá parecer mais rápida, porque o usuário pode começar a fazer algo enquanto a aplicação carrega uma imagem ou um comentário para um post, por exemplo. Em segundo lugar, simplifica a experiência, removendo estados e distrações desnecessárias. O aplicativo parecerá mais simples e mais amigável.

Um ótimo exemplo da aplicação desse conceito é o Like do Instagram, que mesmo sem conexão com a Internet você consegue executar a ação de Like em um Post, esse exemplo é uma relação dos tópicos do Post todo, pois temos a escolha de quais dados persistir, o funcionamento Offline e a fila de ações a ser executada.

E chegamos ao fim!

O post vai acabando por aqui, espero que tenha conseguido passar para vocês um pouco do planejamento de uma aplicação com a arquitetura Offline First, principalmente porque isso foi uma das coisas em que, juntamente com o Diego, fiz recentemente no planejamento de um curso.

Espero que vocês tenham gostado do post, e por isso peço que deixe seu comentário e não deixe de compartilhar com seus amigos 😉

Abraço!