svelte icon indicating copy to clipboard operation
svelte copied to clipboard

<Todo bind:this={todo.ref} ... /> leads to undefined when todos array is mutated

Open michael opened this issue 1 year ago • 3 comments
trafficstars

Describe the bug

While initially all todo.ref class fields are set properly, as soon as I mutate the todos array (e.g. when doing todos.splice(1,0, new Todo('Clean toilet')) the todo.ref is undefined. I had a really hard time reproducing this, as for other indexes it works (like when I do todos.push(new Todo('Clean toilet')).

Reproduction

https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE51UwY7bOAz9FcJbwA4Q2N1rmgx221MPu5ddoIfJoNVYdKyOQhkSPdnC8L8vKNlOMkinRQ9JFOqRIh8fOWSNsRiyzf2QkTpitsn-7LpsnfG3Tv6EZ7SM2ToLrve1WLah9qbjuz3t2Rw75xn-ddr95TRaaLw7Ql5Wi6VMAcqvIX_3wuMKPOGuQAOwqZ9gTMD9lMs-ixj5WGRgp12AHbwJrBiL-3jHhKdzVkX-SYUWtAkthny1vg0xHUJjnfPfQ7z3hg7gegb2KrT5SlAPK8mG9qzCN6qh6alm4wiU1p8JT58lvWIFQ4pYVWAooI-QzgUjh3RVOwosNtjB7--gqqA4GW7Fsos1lhbpwC0YhpPzT0EeBoBEQBk6a2oskgnEaz2f3y6nFwV9sKgI2BmLHIsWzCqxy-lbnZTh2IZiFZOK_3tiY4FbhLpVdEBoVYDOu04dFKMGRRpqd-wcIXGAVj0jPCISeCSNHvXCxj-I8GhIb7g1YTdILaXHZgRDcCGLM0XOYmndociX-OCxAddAY3xIctjk68TK_dsHiVa2aK0rVnNpVQXyHKD3zgd4xFr1ASeXzoXoBCZATxobQ3O-r78v5EoIMOGcwBztOoVxT9vqPEa0feyZHYGj2pr6aTdcqme8-yDGbZVAd3safkNVt5PyVYiHNZhRQm_jZEUiR6gEXAl43FO2zo5Om8agzjbsexzXy8iL0y_M_Cevug791SBPtlvz_JEI_dXkL5YreBqFIbE5ymyLtkIh5F3zdvFgzG2bwmzOzUm6Wt4ZZ2puiq66E91vq-uwPyLucs-dSfwaLgnE_yIDGhvVW4baqhDSEoybQaKdd1ieJ5mIsF4aZ3p8X7PzhTgu20XKKadQ8jNrbXytgoWaX-j_9zf4ay2U-4mPZV1O4zFX4pF7T_AlmtMrb1KfJOvxy-0p0uY5Zvi3OuIGLvAClMsfdXLq-c8yMRdZt8Zqj_S6VidposXjWZi5Ns_5GKMNf6TtuIQrVqnCK78bNTyM_wMV3w_ixQcAAA==

Logs

running Svelte compiler version 5.0.0-next.110
103Fetch finished loading: GET "<URL>".
VM351 about:srcdoc:96 component ref of first todo: hello from Wash dishes
playground:output:3492 Uncaught (in promise) TypeError: todos[pos].ref.hello is not a function
    at HTMLButtonElement.add_new_todo (playground:output:3492:63)

System Info

System:
    OS: macOS 14.2.1
    CPU: (8) arm64 Apple M2
    Memory: 107.00 MB / 8.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.10.0 - /usr/local/bin/node
    npm: 10.2.3 - /usr/local/bin/npm
  Browsers:
    Chrome: 124.0.6367.62
    Safari: 17.2.1
  npmPackages:
    svelte: 5.0.0-next.110 => 5.0.0-next.110

Severity

blocking all usage of svelte

michael avatar Apr 21 '24 11:04 michael

With a keyed each block it works: https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAA51UTY_TMBD9K6OAlESqEriWdsXXhQNcAHHYrsAbTxqz7jiyJ1tWUf47sp2kDZQFcWjrjt98Pb-ZPqmVRpesr_uExAGTdfKqbZNVwg-t_-PuUTMmq8SZzlbesnGVVS1f7WjH6tAay_DJSPPeSNRQW3OAtChnSxEDFN9d-uIXjwV4xC1APbCq7mCIwN1Yyy4JGP_RyMBGGgdbeOpYMGbX4Y4Jj6eqsvSLcA1I5Rp0ab66DFEtQq2NsX9CvLaK9mA6BrbCNWnuUTe5r4Z2LNwDVVB3VLEyBELKr4THr768LIc-RixLUOTQBkhrnPKHeFUZcuxtsIXnL6AsITsqbrxlG3osNNKeG1AMR2PvnE8MAJGAwrVaVZhFE3iv1XR-Np9-aeiNRkHARmnk0LTH5JFdjt_iKBSHZ8jyUFT43xErDdwgVI2gPUIjHLTWtGIvGCUIklCZQ2sIiR004h7hFpHAIkm0KGc2PiLCrSK55ka5be97KSzWAyiCM1mcKDIaC232WTrHB4s1mBpqZV2UwzpdRVaun934aEWDWpssn1orS_DpAK011sEtVqJzOLq0xgUnUA46klgrmup9PL8n14cA5U4FTNGWJQw72pSnMaLNbcdsCAxVWlV32_5cPcPVG2_clBF0taP-CYqqGZUvXDisQEEW6FMyH3ySTZixQOkApXcrvduwo2SVHIxUtUKZrNl2OKzm4fdO_zH9X6xoW7SLkR5tlyb7HRHaxQ6YLQt4HIo-8jr4Kfcqc5mnccngWcJQ2yaGWZ-eKSpszjNM1FyUX3nlJ2BTLsP-jbjzjXci8bs7JxB_BAYk1qLTDJUWzsV1GHaEj3baZmkaBeMl9ptRSdhCZR9aNoUVJM3h8-d3b7NpIQXubFexsZmPOi8h32sx5vE_kySHx9qbefsPcfx50T_2vv5-JGvequMUTZ1Y5M4SfAvmmOVpfERf9fDt8rBJdR8q_CAOuIYzvAf6y7898yiIf2ViarJqlJYW6XEhj7pFjYeTalOp7tMhROtfxiU6h8vy2OHC70IPN8NP41USPewHAAA=

This also kinda explain why position matters. At .length you are pushing in a completely new position. With splice you are "replacing an old one" so the component at position 2 is not unmounted but the prop is replaced and the same goes with the component inside. Is the same component so bind:this is probably not triggered.

Not sure if this is still a bug tho.

paoloricciuti avatar Apr 21 '24 12:04 paoloricciuti

Thanks a lot for spotting this! We can of course provide a unique key when that solves the issue. Still, I guess I'd expect this to work without providing a key too (with less efficient reconciliation).

So leaving this open for the Svelte team to decide if there's action needed (could be a fix, or a warning?).

Attaching our Twitter discussion for context:

https://twitter.com/PaoloRicciuti/status/1782036498780414003

michael avatar Apr 21 '24 13:04 michael

Reduced reproducible.

The behavior is the same in Svelte 4, it doesn't work there, too.

I'm inclined to mark this as "works as designed". The reason is that the each array by default is checking the array index for whether or not the whole thing needs to be recreated. Since the indexes only increase, the existing ones never change and so bind:this has no indication to update.

dummdidumm avatar Apr 29 '24 10:04 dummdidumm

Yeah, this is working as intended. Using keyed each blocks is always the preferred way to handle stateful content FWIW.

trueadm avatar Jun 04 '24 19:06 trueadm