FilamentF
Filament•17mo ago
Grégoire

Looping through multiple Livewire components containing Filament Action crashes Firefox/Safari

đź‘‹ Hi Filament builders!

Context
I'm migrating my action modals from Wire Elements Pro (Modal) to Filament Actions to achieve a more consistent UI and reduce code maintenance.

On a search pages, I'm implementing a feature where users can perform actions for each card by opening a dropdown that lazy loads options including an Action.

Problem
I've discovered that loading a certain number of Livewire components containing Filament action buttons causes the page to crash on both Firefox and Safari. The error messages differ between browsers (see bellow). Chrome appears unaffected by this issue.

Removing InteractsWithActions solves the problem (but the action is not usable anymore).

Minimum example
@for($i = 0; $i < 40; $i++)
<card>
  [...]
  <dropdown>
      <trigger />
      <x-slot:body>
          <livewire:test2-submenu lazy>
      </x-slot>
  </dropdown>
</card>
@endfor

#[Lazy]
class Test2Submenu extends Component implements HasForms, HasActions
{
  use InteractsWithActions, InteractsWithForms;
  public function testAction()
  {
    return Test2Action::make('test');
  }
}

Error messages
Firefox (after reloading within 10s)
  • Too many calls to Location or History APIs within a short timeframe.
  • Uncaught DOMException: The operation is insecure.
    Safari
  • SecurityError: Attempt to use history.replaceState() more than 100 times per 10 seconds
Reproducible repo
The Firefox issue seems to be related to Livewire itself and has been discussed here, but no solution is currently available. I've created a simple example repository to reproduce the issue. The code is available on GitHub, and you can access the demo on the /test2 route.

Question
👉 I may not be using Filament Actions as intended. I'd appreciate your thoughts on this issue and any suggestions for working around it.

---
image.png
Solution
@Grégoire I've found a solution that works even when leaving the InteractsWithActions.
This is a temporary solution, allowing you to space out requests if they're too close to each other.
Just add this JS script to your page:

@push('scripts')
    <script>
        const original = window.history.replaceState;
        let timer = Date.now();

        let timeout = null;
        let lastArgs = null;

        window.history.replaceState = function (...args) {
            const time = Date.now();
            if (time - timer < 300) {
                lastArgs = args;

                if (timeout) {
                    clearTimeout(timeout);
                }

                timeout = setTimeout(() => {
                    original.apply(this, lastArgs);

                    timeout = null;
                    lastArgs = null;
                }, 100);

                return;
            }

            timer = time;

            original.apply(this, args);
        };
    </script>
@endpush
Was this page helpful?