Experiment hypothesis:
We can keep actual R objects around for a while thanks to R's
[un]serialze()
andlocalForage
Experiment parameters:
localForage
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}).
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:
status-message
: (you know this one by now)simple-message
: Basic message displayer without much adornmentaction-button
: Generic action button (pass in a label and click handler)data-frame-view
: It doesn't validate that you passed in a valid data frame (i.e., a full WebRDataJsNull
data.frame
structure), but if you do pass one in with a label and columns to display, it'll toss out a basic scrollable div
with said table. It could be way more generic, but it should be sufficient for you to riff from.Go forth and persist all the things!
Source is on GitHub
Brought to you by @hrbrmstr