Skip to content

svelte 5: can't modify an array passed as a prop to a child component #10037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wires5210 opened this issue Dec 30, 2023 · 3 comments
Closed

Comments

@wires5210
Copy link

Describe the bug

if i pass an array declared with $state to another component, then try to modify it, i get an error saying:

Non-bound props cannot be mutated — to make the `0` settable, ensure the object it is used within is bound as a prop `bind:={...}`. Fallback values can never be mutated.

it works if i use bind:, but as far as i understand, bind: is supposed to be used for when the child component is expected to modify the prop - the error above happens even if the array is modified in the parent component

this happens whether the array is modified with methods like .push(element), or through reassignment like array = [...array, element]. though the latter works when using $state.frozen instead of $state when declaring the array

Reproduction

repl link

  • App.svelte:
    <script>
      import Child from './Child.svelte';
    
        let array = $state([1,2,3,4]);
    
        const addNew = () => {
      	  array.push(0)
        }
    </script>
    
    <button onclick={addNew}>add</button>
    <Child {array} />
  • Child.svelte:
    <script>
        let { array } = $props();
    </script>
    
    {#each array as number}
        <p>{number}</p>
    {/each}

Logs

No response

System Info

mac os/chrome

Severity

annoyance

@dummdidumm
Copy link
Member

There's something inside the each block logic which writes to the user array which then fails. It's related to the lazy_property logic that was added for proxies.

@ptrxyz
Copy link

ptrxyz commented Jan 10, 2024

Funny enough that when you make it a keyed each block it works:

// all good
{#each array as number (number)}
...
// not good
{#each array as number}

dummdidumm added a commit that referenced this issue Feb 2, 2024
The readonly dev time validation results in false-negative object equality checks: The original object is proxified and thus comparisons could be between the unproxified and proxified version, which will always return false. Fixes #10372

There's also the problem that an object could be passed into a component but then passed upwards into shared state. At that point, it should no longer be readonly, but it's not possible to statically analyze these points. Fixes #10372

Lastly, the each block logic mutates an internal array and that also throws errors with the readonly logic. Fixes #10037
@dummdidumm
Copy link
Member

Fixed by #10422

Rich-Harris added a commit that referenced this issue Feb 20, 2024
* fix: remove readonly validation

The readonly dev time validation results in false-negative object equality checks: The original object is proxified and thus comparisons could be between the unproxified and proxified version, which will always return false. Fixes #10372

There's also the problem that an object could be passed into a component but then passed upwards into shared state. At that point, it should no longer be readonly, but it's not possible to statically analyze these points. Fixes #10372

Lastly, the each block logic mutates an internal array and that also throws errors with the readonly logic. Fixes #10037

* reinstate tests

* track ownership of state and mutations

* working?

* remove old changeset

* tidy

* error

* simplify

* fix

* fix

* fix

* tidy

* make it a warning

* rename test

* remove unused test

* update tests

* slap ts-expect-error everywhere, because its too finicky otherwise

* oops

* oh no the hall monitor is here

* only call add_owner in dev

* only owners can transfer ownership

* simplify

* fixes

* tidy up

* fix type error

* while we're at it

* rename file

* rename functions

* add some comments

* move ownership checking logic

* ugh eslint

* more detailed message

* only add filename in dev

* comment

* update tests

* move more code

* undo change to sourcemap tests

* allow proxy to have multiple owners

* use SignalDebug

* i was doing this all wrong

* tidy up

* implement inheritance

* fix

* tidy up

* update filename stuff

* changeset

---------

Co-authored-by: Simon Holthausen <[email protected]>
Co-authored-by: Rich Harris <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants