Home > Software engineering >  Using IndexedDB to store form state
Using IndexedDB to store form state

Time:06-02

I'm developing an SPA with a long multi-page form. Loosing the form state upon reload is a really bad UX. Especially since the form is quite sizeable.

Since this is an SPA, browser won't be able to restore form state after a reload. And even if it could it's a multi-page form where only a subset of fields is displayed (although it may fill in hidden inputs, as I come to think of it).

I could've used history state to store the current form state, but I want back button to be able to go back to a previous form page without clearing the progress on the current page.

I could've used sessionStorage, but I need to store files and images as part of a form. it's synchronous.

I could've synced state with the server, but I don't want to introduce such complexity onto the backend, especially as it feels like the problem that could be solved completely on the client.

I was thinking of using IndexedDB. But the problem arises when the form state is inaccessible and needs to be cleared (e.g. due to user closing a tab).

One way I could think of solving it, is through service worker, cause it has the list of all currently open tabs (via self.clients.matchAll() API), and periodically to do garbage collection on data that is associated with now missing client id.

Unfortunately that is quite a complexity jump to introduce a proper service worker support for an app. I'd rather delay the introduction of service worker til I would be required to implement offline-first support.

I was wondering, if there is different solutions to the form persistence problem in SPAs, that would require less development resources to introduce.

CodePudding user response:

I'm assuming you've seen the "If I can't run asynchronous APIs in the frozen or terminated states, how can I save data to IndexedDB?" guidance in the blog post about the Page Lifecycle API, since using a service worker to persist data is one of the options listed there.

The other option, though, is to make use of the commit() method on the IDBTransaction object, to perform an effectively synchronous write-only IDB operation when a tab is being closed. The article mentions that commit() wasn't widely supported at the time, but since it was originally written, browser support has improved substantially.

CodePudding user response:

I've thought over countless different methods of employing such a thing, but to no avail.

pagehide, beforeunload, and unload are not fired reliably enough (especially on mobile).

clients.matchAll() does not return discarded clients (which are abundant on mobile), meaning that their session could still be restored, whereas my service worker would've thought that they are gone forever (and garbage collected form state).

I am probably going to resort to time-based (or LRU) eviction strategy for form states. Either checking periodically for entries not used, for e.g., a year or so, or when I'm running close-ish to the provided quota – trigger LRU eviction.

Ideally this should be easily solved by having session-scoped IndexedDB instance. As I remember Jake Archibald mentioned he wants something like this on a web platform. I think this should be a great addition, and honestly not many people are discussing this problem in web-dev community (as I wasn't able to found the answer to (nor discussions about) my problem by just googling it). I honestly think we should strive to improve UX in such ways. Maybe I should bug Jake about it some more to let him (and the web working group) know there is indeed need for it, and... who knows... maybe some day we will see it implemented.

As for now, this is unfortunately an unsolved question, as I'm still not 100% happy with the time based eviction.

  • Related