Nifty way to (fully) client-side cache a turbo-frame

I ran into an issue with an API I am using that in its terms forbid any kind of server side caching, but allowed client side caching.

I ended up doing a simple trick in my Rails app:

<!-- index.html.erb -->
<turbo-frame id="api-results" src="/api_results"/>

<!-- api_results.html.erb -->
<turbo-frame id="api-results">
  <div data-controller="cache" data-cache-duration-value="60">
  <!-- ... all the results -->
</turbo-frame>

And two reusable Stimulus controllers:

// cache_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static values = { duration: Number }

  connect() {
    var parent = this.element.parentNode

    if (parent.src) {
      parent.dataset.controller = "cached"
      parent.dataset.cachedFetchAfterValue = Date.now() + (this.durationValue * 1000)
      parent.dataset.cachedSrcValue = parent.src
      parent.src = ''
    }
  }
}

// cached_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static values = { fetchAfter: Number, src: String }

  connect() {
    if (Date.now() >= this.fetchAfterValue) {
      this.element.src = this.srcValue;
    }
  }
}

Whenever content is loaded that I want to cache client side, I add the prefix div, which, when connected, wipes the src of the parent turbo-frame, and adds a controller, that, when connected, checks if it needs to re-add it after x seconds.

3 Likes

This is really interesting – can you expand on this a bit? I’m curious to understand how long the cache “lives” for in conjunction with navigating around the app? If a user were to leave this page, and then come back, Turbo Drive should fetch the fresh version of the page which would lead to a non-cached version?

This is very timely for me since I’ve was literally just playing with Turbo Frames this morning, and have found that adding data-turbo-permanent to a Turbo Frame will maintain the state across pages, which can have a nice caching affect. I think perhaps the two approaches can play very nicely together, but I want to understand yours better.