🧪 🕸️ 🫙 WebR + localForage

When you need to keep R stuff around for a bit.

Experiment hypothesis:

We can keep actual R objects around for a while thanks to R's [un]serialze() and localForage

Experiment parameters:


Fiddling With Local Storage


If nothing else, you sure are a persistent one, hrbrmstr

Aye, that I am!

I'm willing to bet you (almost) never use R's super cool serialize() function (directly). That function takes an R object and transforms it into a format that can be persisted outside R. If the second parameter to it is NULL, then you get back a raw vector. It has a corresponding unserialize() which does what you think it does.

Modern browsers have lots of ways to store things locally. You can persist data as well, meaning you can store stuff — like R objects! — to be used later. This could come in handy!

Using those storage APIs directly is, quite frankly, painful. However, that localForage library abstracts the pain away, providing familiar key/value idioms to store, retrieve, and remove persisted copies of any data. That means we can do something like this:

const res = await R`serialize(mtcars, NULL)`
await lf.setItem('mtcars', res.values)

We can get that back from local storage via:

const opts = {
  env: { mtcars_serialized: await lf.getItem('mtcars') }
}
const res = await webR.evalR("unserialize(as.raw(mtcars_serialized))", opts)

And, we can get rid of it just as easily:

await lf.removeItem('mtcars')

If you open up Developer Tools, you'll see the bytes for the serialized first row of mtcars (I did not want to clutter up this page with hex string output). res.values contains the entire copy of mtcars in that raw format.

You should not go crazy with this feature, meaning try not to DoS your visitors with a gigantic amount of local storage. And, you will need to read up on localForage and web client storage in general to grok the nuances, limits, and "gotchas".

Reading up on R's [un]serialize() is also required, since there are limits to what it can do, especially with complex objects (an example of which might be parsed HTML/XML via {xml2}).

Some JavaScript Stuff

main.js does all the hard work (it is also annotated), so I avoided putting large swaths of code blocks in this write-up.

There are four oddly (for me) reusable Lit web components included:

FIN

Go forth and persist all the things!

Source is on GitHub

Brought to you by @hrbrmstr