Conheça as práticas recomendadas para sincronizar o estado do aplicativo entre o IndexedDB e as bibliotecas de gerenciamento de estado mais usadas.
Quando um usuário carrega um site ou aplicativo pela primeira vez, geralmente há um trabalho considerável envolvido na construção do estado inicial do aplicativo que é usado para renderizar a interface. Por exemplo, às vezes o app precisa autenticar o usuário no lado do cliente e, em seguida, fazer várias solicitações de API antes de ter todos os dados necessários para exibição na página.
Armazenar o estado do aplicativo no IndexedDB pode ser uma ótima maneira de acelerar o tempo de carregamento para visitas repetidas. O app pode ser sincronizado com qualquer serviço de API em segundo plano e atualizar a interface com novos dados de forma lenta, usando uma estratégia stale-while- revalidate.
No entanto, ao usar o IndexedDB, há muitos pontos importantes a serem considerados que podem não ser imediatamente óbvios para desenvolvedores que não conhecem as APIs. Este documento responde a perguntas comuns e discute algumas das coisas mais importantes a serem lembradas ao manter o estado do aplicativo no IndexedDB.
Manter o app previsível
Muitas das complexidades do IndexedDB vêm do fato de que há muitos fatores que você (o desenvolvedor) não controla. Esta seção aborda muitos dos problemas que você precisa considerar ao trabalhar com o IndexedDB.
As gravações no armazenamento podem falhar
Erros ao gravar no IndexedDB podem acontecer por vários motivos e, em alguns casos, esses motivos estão fora do seu controle como desenvolvedor. Por exemplo, alguns navegadores não permitem gravar no IndexedDB no modo de navegação anônima. Também é possível que um usuário esteja em um dispositivo que está quase sem espaço em disco, e o navegador vai impedir que você armazene qualquer coisa.
Por isso, é fundamental que você sempre implemente o tratamento de erros adequado no código do IndexedDB. Isso também significa que geralmente é uma boa ideia manter o estado do aplicativo na memória (além de armazená-lo), para que a interface não seja interrompida ao ser executada no modo de navegação privada ou quando o espaço de armazenamento não estiver disponível (mesmo que alguns dos outros recursos do app que exigem armazenamento não funcionem).
Os dados armazenados podem ter sido modificados ou excluídos pelo usuário
Ao contrário dos bancos de dados do lado do servidor, em que é possível restringir o acesso não autorizado, os bancos de dados do lado do cliente são acessíveis a extensões do navegador e ferramentas para desenvolvedores e podem ser limpos pelo usuário.
Embora seja incomum que os usuários modifiquem os dados armazenados localmente, é bastante comum que eles os limpem. É importante que o aplicativo possa lidar com esses dois casos sem erros.
Os dados armazenados podem estar desatualizados
Assim como na seção anterior, mesmo que o usuário não tenha modificado os dados, também é possível que os dados que ele tem no armazenamento tenham sido gravados por uma versão anterior do seu código, possivelmente uma versão com bugs.
O IndexedDB tem suporte integrado para versões de esquema e upgrade usando o método
IDBOpenDBRequest.onupgradeneeded(). No entanto, você ainda
precisa escrever o código de upgrade de forma que ele possa lidar com o usuário que vem
de uma versão anterior (incluindo uma versão com um bug).
Os testes de unidade podem ser muito úteis aqui, já que geralmente não é possível testar manualmente todos os caminhos e casos de upgrade possíveis.
Manter o desempenho do app
Um dos principais recursos do IndexedDB é a API assíncrona, mas não se deixe enganar achando que não é preciso se preocupar com o desempenho ao usá-la. Há vários casos em que o uso inadequado ainda pode bloquear a linha de execução principal, o que pode levar à falta de resposta.
Como regra geral, as leituras e gravações no IndexedDB não podem ser maiores do que o necessário para os dados que estão sendo acessados.
Embora o IndexedDB possibilite armazenar objetos grandes e aninhados como um único registro (e isso é bastante conveniente do ponto de vista do desenvolvedor), essa prática deve ser evitada. Isso ocorre porque, quando o IndexedDB armazena um objeto, ele precisa primeiro criar um clone estruturado desse objeto, e o processo de clonagem estruturada acontece na linha de execução principal. Quanto maior o objeto, maior será o tempo de bloqueio.
Isso apresenta alguns desafios ao planejar como persistir o estado do aplicativo no IndexedDB, já que a maioria das bibliotecas de gerenciamento de estado mais usadas (como Redux) funciona gerenciando toda a árvore de estado como um único objeto JavaScript.
Embora o gerenciamento de estado dessa maneira tenha muitos benefícios e o armazenamento de toda a árvore de estado como um único registro no IndexedDB possa ser tentador e conveniente, fazer isso após cada mudança (mesmo que seja limitada/com debounce) vai resultar em um bloqueio desnecessário da linha de execução principal, aumentar a probabilidade de erros de gravação e, em alguns casos, até mesmo fazer com que a guia do navegador falhe ou pare de responder.
Em vez de armazenar toda a árvore de estado em um único registro, divida-a em registros individuais e atualize apenas os registros que realmente mudarem.
Como acontece com a maioria das práticas recomendadas, essa não é uma regra de tudo ou nada. Nos casos em que não é possível dividir um objeto de estado e gravar apenas o conjunto de mudanças mínimo, dividir os dados em subárvores e gravar apenas elas ainda é preferível a sempre gravar toda a árvore de estado. Pequenas melhorias são melhores do que nenhuma melhoria.
Por fim, você precisa sempre medir o impacto no desempenho do código que escreve. Embora seja verdade que pequenas gravações no IndexedDB tenham um desempenho melhor do que as grandes, isso só importa se as gravações no IndexedDB que seu aplicativo está fazendo realmente levam a tarefas longas que bloqueiam a linha de execução principal e prejudicam a experiência do usuário. É importante medir para entender o que você está otimizando.
Conclusões
Os desenvolvedores podem usar mecanismos de armazenamento do cliente, como o IndexedDB, para melhorar a experiência do usuário do aplicativo, não apenas mantendo o estado entre as sessões, mas também diminuindo o tempo necessário para carregar o estado inicial em visitas repetidas.
Embora o uso correto do IndexedDB possa melhorar drasticamente a experiência do usuário, o uso incorreto ou a falha no tratamento de casos de erro podem levar a apps corrompidos e usuários insatisfeitos.
Como o armazenamento do cliente envolve muitos fatores fora do seu controle, é fundamental que o código seja bem testado e processe os erros corretamente, mesmo aqueles que podem parecer improváveis de ocorrer.