question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Not clearing cache when server responds to form submit with text/vnd.turbo-stream.html

See original GitHub issue

SUMMARY

After submitting a non-GET form and server responds with…

  • failure => DOES NOT Clear Cache (CORRECT)
  • success/redirect response => Clears Cache (CORRECT)
  • success/turbo-frame response => DOES NOT Clear Cache (BUG)
  • success/turbo-stream response => DOES NOT Clear Cache (BUG)

DETAILS

When submitting a non-GET forms, turbo clears it’s cache:

class FormSubmission {
  requestSucceededWithResponse(request, response) {
      //...
      this.delegate.formSubmissionSucceededWithResponse(this, response);
    }
  }
}

class Navigator {
  async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
    if (formSubmission == this.formSubmission) {
      const responseHTML = await fetchResponse.responseHTML;
      if (responseHTML) {
        if (formSubmission.method != FetchMethod.get) {
          this.view.clearSnapshotCache();
        }
        // ...
      }
    }
  }
}

However, if the server responds with header Content-Type: text/vnd.turbo-stream.html, then the above logic is skipped and the cache is not cleared.

class StreamObserver {
  constructor(delegate) {
    //...
    this.inspectFetchResponse = event => {
      const response = fetchResponseFromEvent(event);
      if (response && fetchResponseIsStream(response)) {
        event.preventDefault();
        this.receiveMessageResponse(response);
      }
    };
    //...
  }
}

class FetchRequest {
  async receive(response) {
    const fetchResponse = new FetchResponse(response);
    const event = dispatch("turbo:before-fetch-response", {
      cancelable: true,
      detail: {
        fetchResponse: fetchResponse
      },
      target: this.target
    });
    if (event.defaultPrevented) {
      this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
    } else if (fetchResponse.succeeded) {
      this.delegate.requestSucceededWithResponse(this, fetchResponse);
    } else {
      this.delegate.requestFailedWithResponse(this, fetchResponse);
    }
    return fetchResponse;
  }
}

When StreamObserver detects fetchResponseIsStream it calls event.preventDefault(). Then in FetchRequest#receive follows the condition event.defaultPrevented which calls FormSubmission#requestPreventedHandlingResponse instead of FormSubmission#requestSucceededWithResponse (mentioned at the beginning of this post). FormSubmission#requestPreventedHandlingResponse does not clear cache.

Is this intentional? For us it feels like a bug.

Testing a possible solution, the following change clears the cache when the server responds with a turbo-stream:

- requestPreventedHandlingResponse(request, response) {
+ async requestPreventedHandlingResponse(request, response) {
    this.result = {
      success: response.succeeded,
      fetchResponse: response
    };
+   const responseHTML = await response.responseHTML;
+   if (responseHTML && this.method != FetchMethod.get) {
+     this.delegate.view.clearSnapshotCache();
+   }
  }

We discovered this issue while using data-turbo-permanent, which cache even with an updated server response.

You can use the following turbo:submit-end event listener workaround:

const FetchMethod = { get: 0 };
const StreamMessage = { contentType: 'text/vnd.turbo-stream.html' };
document.addEventListener('turbo:submit-end', async ({ detail }) => {
  const nonGetFetch = detail.formSubmission.fetchRequest.method !== FetchMethod.get;
  const contentType = detail.fetchResponse.response.headers.get('content-type');
  const fetchResponseIsStream = contentType.startsWith(StreamMessage.contentType);
  const responseHTML = await detail.fetchResponse.responseHTML;
  if (detail.success && nonGetFetch && fetchResponseIsStream && responseHTML) {
    Turbo.clearCache();
  }
});

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:8 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
tleishcommented, Sep 29, 2022

@domchristie - I honestly do not know the purpose of using responseHTML. I added it when mimicking logic used in used to Navigator#formSubmissionSucceededWithResponse which checks for the existence of responseHTML in order to clear the cache.

see: https://github.com/hotwired/turbo/blob/1ec72ac3236038af50a0010f0821e0664eeac087/src/core/drive/navigator.ts#L86-L105

0reactions
domchristiecommented, Dec 13, 2022

I think my preference would be to duplicate view.clearSnapshotCache() where the response is handled:

  1. standard form POST (Navigator#formSubmissionSucceededWithResponse, already implemented)
  2. frame form POST (FrameController#formSubmissionSucceededWithResponse)
  3. stream POST (Session#receivedMessageFromStream?—this may require updating a method signature to include the response method)

That way FormSubmission is just responsible for submitting the form, and not clearing view caches.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Turbolinks to Turbo upgrade has broken form redirection
Turbo makes a turbostream request when submitting a form, but if the response is a redirect, it expects it to be text/html in...
Read more >
Navigate with Turbo Drive - Hotwire Turbo
Turbo Drive accelerates links and form submissions by negating the need for full page reloads.
Read more >
Turbo Drive Essentials - Akshay's Blog
The core value proposition of Hotwire is that all the logic lives on the server, and the browser deals just with the final...
Read more >
Progressive Enhancement with Hotwire - INNOQ
Rendering websites on the server and then using “progressive ... want into your response, assign the content type text/vnd.turbo-stream.html ...
Read more >
Server and Browser Cache - Support Center - WP Engine
Learn about WP Engine server caching and how to purge cache. ... If you're having issues with a form not submitting, or use...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found