livewire icon indicating copy to clipboard operation
livewire copied to clipboard

[Failing Test] Fingerprint of null issue

Open joshhanley opened this issue 3 years ago • 1 comments

Currently if you have nested components in a loop and you don't have a wire:key, you get the following error

image

The issue is, when morphdom tries to add a new node and create a new component, the node already has a wire:id on it instead of wire:initial-data and so it errors out when it tries to read fingerprint from initial data.

There is two ways we can approach fixing this:

  • Do a check in the new component method and throw a nicer error if initial data is not set - like "Make sure you have a wire:key on your components"

  • The other way, involves digging deep into the morphdom stuff finding out why it's trying to add a component on an element that already has wire:id on it

Hope this helps!

joshhanley avatar Sep 10 '21 03:09 joshhanley

Ok, here's what's going on with this failing test:

Initially the following HTML loads: (this is a component with 5 subcomponents rendered in a loop without a :key/key()):

<div dusk="values">
    <div wire:id="VE0BzNpNKQf4YfXlKJof">Value 1</div>
    <div wire:id="YjwOvJk5trhThtQg1O28">Value 2</div>
    <div wire:id="SViBCPgKzkFg6ISv5bva">Value 3</div>
    <div wire:id="TAA1Jnls2KXg9knrHSO2">Value 4</div>
    <div wire:id="19KSw0gGQTeRTHCyC9CJ">Value 5</div>
</div>

Now after refreshing the component (or performing any action on it), we get the following HTML from the server to morph to:

<div dusk="values">
    <div wire:id="19KSw0gGQTeRTHCyC9CJ"></div>
    <div wire:id="19KSw0gGQTeRTHCyC9CJ"></div>
    <div wire:id="19KSw0gGQTeRTHCyC9CJ"></div>
    <div wire:id="19KSw0gGQTeRTHCyC9CJ"></div>
</div>

There are two things to note about the returned HTML:

  • All the divs are empty because Livewire doesn't render subcomponents after they've been rendered once
  • All the "wire:id"s have been set as the same one because the @foreach that rendered them wasn't keyed so it wasn't able to track them

Now, after Livewire attempts to morph, it will error out and only get this far in the HTML morphing:

<div dusk="values">
    <div wire:id="19KSw0gGQTeRTHCyC9CJ">Value 5</div>
    <div wire:id="VE0BzNpNKQf4YfXlKJof">Value 1</div>
    <div wire:id="YjwOvJk5trhThtQg1O28">Value 2</div>
    <div wire:id="SViBCPgKzkFg6ISv5bva">Value 3</div>
    <div wire:id="TAA1Jnls2KXg9knrHSO2">Value 4</div>
    <div wire:id="19KSw0gGQTeRTHCyC9CJ"></div>
</div>

Because there is a duplicate here, one component is "added", when Livewire tries to initialize that component it doesn't find any data attached and errors out with that error you're reporting.

There's a few things I see us doing to fix this:

  1. Provide a better error message in this case (should be easy): something telling users that something went wrong with the morph and that they need to make sure there are keys in loops and point them to a troubleshooting page
  2. Investigate if there is a way to preserve the keys on the backend (probably not)
  3. Throw a backend error somehow if there is a loop in the backend with no keys in nested components (would probably require overwriting @foreach and doing wizardry)

That's all for my initial investigation. I will dig further when I have more time and maybe address some of these. Thanks for the good test @joshhanley

calebporzio avatar Sep 19 '21 13:09 calebporzio

Closing as this shouldn't be an issue in V3.

joshhanley avatar Nov 17 '22 01:11 joshhanley