modal icon indicating copy to clipboard operation
modal copied to clipboard

Anomally on modal close & reopen quickly

Open razvan-p opened this issue 2 years ago • 14 comments

This seems to be a bug on modal pro component. Steps to reproduce:

  1. Click to open the modal:
<button
    type="button"
    onclick="Livewire.dispatch('modal.open', { component: 'create-model' })"
>
    Open modal
</button>
  1. Press the submit button. My code looks like:
<x-wire-elements-pro::bootstrap.modal on-submit="save">
    ...
    <x-slot name="buttons">
        <div class="mt-2">
            <button type="submit">
                Save
            </button>
        </div>
    </x-slot>
</x-wire-elements-pro::bootstrap.modal>
public function save()
{
    $this->authorize('save', $this->form->model);

    $model = $this->form->store();

    $this->close(
        andDispatch: [
            'modelSaved',
        ]
    );
}
  1. Quickly click to open the modal again.

The error in console:

Uncaught Snapshot missing on Livewire component with id: NWq3XntmBYjxHS3l8mVr
Screenshot 2024-01-10 at 12 39 03

PS: If the error doesn't appear on the first try, repeat again all the steps.

razvan-p avatar Jan 10 '24 10:01 razvan-p

Hi there, This error often occurs when Livewire is unable to morph content. Are you using a placeholder / lazy loading for example? You will need to ensure the root element is of the same time.

<x-wire-elements-pro::bootstrap.modal> will return a <form> element. If you have a placeholder that is a <div> it will trigger this error. Would you mind checking if this is the case?

PhiloNL avatar Jan 10 '24 16:01 PhiloNL

I don't use any placeholder. My modal looks like:

<x-wire-elements-pro::bootstrap.modal on-submit="store" :content-padding="false">
    <x-slot name="title">
        {{ __('Title') }}
    </x-slot>

    <div class="col-md-12 scrollable-inside-fullscreen-modal">
        <div class="row g-3 mt-3">
            <div class="col-12 px-3">
                <strong>{{ __('Product') }}</strong>
            </div>
            <div class="col-md-12">
                {{ $form->product->name }}
            </div>
        </div>
        ...
    </div>

    <x-slot name="buttons">
        <div class="mt-2">
            @can('delete', $form->product)
            <button type="button" wire:loading.attr="disabled" wire:click="delete">
                <i class="fas fa-fw fa-trash-alt"></i>
            </button>
            @endcan
            <button type="submit" class="btn btn-tertiary btn-lg mx-2" wire:loading.attr="disabled">
                {{ __('Save') }}
            </button>
        </div>
    </x-slot>
</x-wire-elements-pro::bootstrap.modal>

razvan-p avatar Jan 10 '24 20:01 razvan-p

We also have this problem. The issue also arise when the modal is simply closed (button, click-away or ESC key) and quickly reopened. Thank you for your time. I bought the PRO license and this is a BIG issue for us.

Sometime, the JS error is not thrown, but the modal is not loading even if the overlay with unfocused effect is displayed. The page has to be reloaded.

Issue happens with x-wire-elements-pro::tailwind.modal and with our custom modal component

jmitech avatar Jan 13 '24 17:01 jmitech

Can you provide a simple example component that reproduces the problem? I've been unable to reproduce the issue following the steps mentioned: https://d.pr/v/Oid3ne

PhiloNL avatar Jan 13 '24 18:01 PhiloNL

Please check this recording. If needed I can provide the code related to the submitted form which contains multiple select2 elements, foreach loops, if/else conditions and alpine js code.

https://github.com/wire-elements/modal/assets/105241697/46440e75-7bba-4d04-ba79-0c834a9b43e1

razvan-p avatar Jan 13 '24 20:01 razvan-p

Can you provide a repository with a fresh Laravel & Livewire install that reproduces the issue? There are so many factors at play due to third-party dependencies that it's very difficult to figure out the problem without having access to a working app that contains an example.

PhiloNL avatar Jan 13 '24 21:01 PhiloNL

Can you provide a simple example component that reproduces the problem? I've been unable to reproduce the issue following the steps mentioned: https://d.pr/v/Oid3ne

I'm suspecting some kind of timing issue with the DOM/Livewire at removal/creation of modal components...

I have identified two problems,

In the video, since your test page and test modal are very lite the issues aren't happening. I'm pretty sure you can still recreate problem 1 with this environment by using the ESC key to close the modal then quickly click on the trigger link (as fast as you can). At some point, you will get the blurred overlay showed without the actual modal, user has to refresh page. That's problem 1 for me.

I agree this is not a real-world action for a user to do, but, it can happen. Also, we had some workflows that made it happen programmatically too.

As for problem 2 (OP's exact issue i.e. Uncaught Snapshot missing on Livewire component with id: y8ItIRaMcNPXta5npwZE), it looks like it's triggered by the same mechanism (re-opening a modal too quickly) but we only could reproduce with Big livewire components that are [as I imagine] taking more time to be evaluated client-side. If I wait 1 or 2 seconds after the modal is closed, I can always re-open it without errors. But if I click too quickly, scripting stops and outputs the dreaded error. Note that the issue also happens when another record's modal is requested (does not have to be the same trigger btn/modal).

To recap:

  1. Problem 1: When user tries to re-open any modal too quickly. Modal doesn't pop. Blurred overlay is displayed. No error in console. User has to refresh.

  2. Problem 2: When user re-opens an heavy component's modal too quickly. Modal doesn't pop. Blurred overlay is shown (not always). Error in console and scripting breaks. User has to refresh page.

So, is problem 2 linked to problem 1? Are these big components of ours misconfigured or poorly written (hydration/datatypes/wire:keys/etc)?... maybe, probably! But is this the cause of problem 2 or not? That's what I'd like to know.

Hope it helps. I love wire-elements btw!

Thank you very much, Jim

jmitech avatar Jan 16 '24 17:01 jmitech

Thanks @jmitech So far I haven't been able to reproduce this issue so I have an assumption about what might be going on. When all modals are closed the state is destroyed (a request is made to the server to do this). A new modal is requested before the previous request has finished, which causes the state to be destroyed after the new modal has opened. The question is, why is the request to destroy the modal slower compared to opening a new modal? Because the latter should be slower.

Would you mind checking your devtools and watch the update events and see if this is the case? For me, the open request always seems to be slower in milliseconds compared closing.

Thanks!

PhiloNL avatar Jan 23 '24 10:01 PhiloNL

In my situation, this issue happens because a second modal is opened while resetState is in flight.

  1. setActiveComponent is called to set the component and show the modal
  2. setShowPropertyTo(false) is later called to close the modal
  3. setShowPropertyTo invokes $wire.resetState after 300ms, which starts an HTTP request with the current component state
  4. setActiveComponent is called to set the component and show a second modal before the request is complete
  5. $wire.resetState's HTTP request completes and updates the component, overwriting the change that was made in step 4, and sometimes causing corruption of the component state.

Because $wire.resetState returns a promise, it is likely possible to make subsequent setActiveComponent calls wait until it is complete first.

27pchrisl avatar Feb 08 '24 10:02 27pchrisl

I've added a click throttle in v4.0.14 that might also solve this problem. It does require you to use the wire:modal or wire:slide-over directives like: <button wire:modal="assets-show, @js(['releaseId' => $release->id)">Open</button>

PhiloNL avatar Mar 30 '24 09:03 PhiloNL

Hi Philo! Adding wire:modal seems to works in that scenarios!. But which is the right way to use "wire:modal" in Blade context calls? Like: <button onclick="Livewire.dispatch('modal.open', {component: 'edit-user', arguments: {'user': 1}})">Open</button> ¿? Thanks!

avennera avatar May 26 '24 22:05 avennera

Hi Philo! Adding wire:modal seems to works in that scenarios!. But which is the right way to use "wire:modal" in Blade context calls? Like: <button onclick="Livewire.dispatch('modal.open', {component: 'edit-user', arguments: {'user': 1}})">Open</button> ¿? Thanks!

Hi Philo! Adding wire:modal seems to works in that scenarios!. But which is the right way to use "wire:modal" in Blade context calls? Like: <button onclick="Livewire.dispatch('modal.open', {component: 'edit-user', arguments: {'user': 1}})">Open</button> ¿? Thanks!

<button wire:modal="edit-user, @js(['user' => 1)">Open</button>

PhiloNL avatar May 29 '24 21:05 PhiloNL

Dear @PhiloNL this solution <button wire:modal="edit-user, @js(['user' => 1)">Open</button> works perfectly on the blade page, but how to use this wire:modal call from Livewire controler ? Correctly for this scenario when i need to open new modal from backend. $this->dispatch('modal.open', component: 'modals.edit-user', arguments: ['uuid' => 'test-1212-123-test']); Thanks in advance Nenad

nenadsijan avatar Jul 02 '24 11:07 nenadsijan

Dear @PhiloNL this solution <button wire:modal="edit-user, @js(['user' => 1)">Open</button> works perfectly on the blade page, but how to use this wire:modal call from Livewire controler ? Correctly for this scenario when i need to open new modal from backend. $this->dispatch('modal.open', component: 'modals.edit-user', arguments: ['uuid' => 'test-1212-123-test']); Thanks in advance Nenad

Hi there! Also what if we open the modal from a non livewire page (where wire:modal directive is not available). For example like this:

<button onclick="Livewire.dispatch('modal.open', { component: 'show-warehouses-modal', arguments: { arg1: 1 } })">
    Manage Warehouses
</button>

More than 50% of my modals are managed like this because I don't need Livewire on every page except of this modal.

razvan-p avatar Jul 02 '24 11:07 razvan-p

I've added a click throttle in v4.0.14 that might also solve this problem. It does require you to use the wire:modal or wire:slide-over directives like: <button wire:modal="assets-show, @js(['releaseId' => $release->id)">Open</button>

Is this available on the PRO version?

nickspringham avatar Jul 15 '24 16:07 nickspringham

I've added a click throttle in v4.0.14 that might also solve this problem. It does require you to use the wire:modal or wire:slide-over directives like: <button wire:modal="assets-show, @js(['releaseId' => $release->id)">Open</button>

Is this available on the PRO version?

Yes

PhiloNL avatar Jul 15 '24 19:07 PhiloNL

Can you provide a simple example component that reproduces the problem? I've been unable to reproduce the issue following the steps mentioned: https://d.pr/v/Oid3ne

This is still an issue for us from what I can see. Can you double click (quickly) the same button in your example project that opens the modal? then when you close that modal you'll just get the blurred background and an Uncaught (in promise) Component not found:##### error in your console.

nickspringham avatar Jul 17 '24 08:07 nickspringham

Can you provide a simple example component that reproduces the problem? I've been unable to reproduce the issue following the steps mentioned: https://d.pr/v/Oid3ne

This is still an issue for us from what I can see. Can you double click (quickly) the same button in your example project that opens the modal? then when you close that modal you'll just get the blurred background and an Uncaught (in promise) Component not found:##### error in your console.

The demo repository still uses wire:click instead of wire:modal, if you use the latter, it should solve the double click issue.

PhiloNL avatar Jul 30 '24 21:07 PhiloNL