Skip to content
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

When passing an imported $state as Object.values() to an external component reactivity does not work #13212

Open
mrh1997 opened this issue Sep 12, 2024 · 13 comments
Assignees
Milestone

Comments

@mrh1997
Copy link

mrh1997 commented Sep 12, 2024

Describe the bug

When I import a $state({...}) and pass it via Object.values(...) as attribute to a sub-component reactivity does not work:

<script>
	import { obj } from "./Data.svelte.js";
       ...
</script>

...
<Component lst={Object.values(obj)}/>

The Problem does not occur when using Object.values(obj) locally or when creating a derived rune (let derivedObj = $derived(obj)) and pass this one to the sub-component.

Reproduction

https://svelte-5-preview.vercel.app/#H4sIAAAAAAAACq1SsU7DMBD9lZNhSKWooYiFkFZClJUiQCyEwU0dmjS1I_tSqCz_O-emNKFdGJiiuzy_9-7uWZYXlTAsfrNM8rVgMbutaxYy3Na-MBtRoaDaqEZnvpOYTBc1TlKZYrGulUa4U_SVQiLkWq0hZcPo0Bq2DCm76T2woOYluA4-5cj3yGFpWjDB80ZmWCgJWtQVz8SjVnUwAOv_pTiblyLDITem-JABMYZgeXwdwjweXYSQxaORG-xkXSqTqPMtk3mDSLRKZlWRrca2x-8IAU9tDa-8aoQBlZNLEkiZJ2oft0TLq4kWnExuCtz2cDCd3T_Dw-wFPpVexUlEQIJ3m6oMju1-gs1OxU8wcNERb37E6-nMD589I8wSTmmAG6DS-eGTRbGZtBuzh17UNm3kCRzdd60WpCUWLEbdCBce4nCw_NdQVMIfmOajA4_hvKadmsDf4dcJ9tY97P-89mLUuS1N36n42iXQm_QZJIMGOYqAkjPyybmkyJxKvLtvssT2IikDAAA=

Logs

No response

System Info

reproduced in Preview

Severity

annoyance

@dummdidumm dummdidumm added this to the 5.0 milestone Sep 12, 2024
@dummdidumm
Copy link
Member

On one hand this is similar to the expectation that objects with a custom toString method that is reactive should somehow be detected - we said we won't do this.

On the other hand, if we detect that the template calls a function that exists in scope, we create the correct code that makes the result reactive.

<script>
  import { obj } from './somewhere.svelte.js';
  import Component from './Component.svelte';

  function foo(o) { return o.length }
</script>

<!-- this will be reactive -->
<Component x={foo(obj)} />

Only in the case of not detecting the function we're making this a static property that is not updating.

I lean towards making any call-expression, even of things not in scope, reactive - but would like more input from other maintainers first.

@trueadm
Copy link
Contributor

trueadm commented Sep 12, 2024

Yeah this is a bug, it's a call expression and should be treated as dynamic because of that.

@trueadm trueadm self-assigned this Sep 12, 2024
@trueadm
Copy link
Contributor

trueadm commented Sep 12, 2024

Okay well I thought it was a bug, as this used to work fine, but it was changed in this PR #12712

@dummdidumm
Copy link
Member

Good find - I would be in favor of undoing part of that: If you pass a variable that is not a global to one of the global functions, bail out and generate a template effect instead. i.e. {location.href} and btoa('foo') are static, but btoa(foo) is not.

@trueadm
Copy link
Contributor

trueadm commented Sep 12, 2024

Good find - I would be in favor of undoing part of that: If you pass a variable that is not a global to one of the global functions, bail out and generate a template effect instead. i.e. {location.href} and btoa('foo') are static, but btoa(foo) is not.

Yep, that sounds logical to me, or we could check to see if the call expression arguments contain an import? A bit like we have has_state, we could check has_import maybe.

@dummdidumm
Copy link
Member

dummdidumm commented Sep 12, 2024

I'm not sure what other globals could theoretically do. If you have a global and it accesses a property of a value which happens to be reactive, do we want to rerun it? E.g.

let value = { a: state_in_here }
...
JSON.stringify(value); // does access value.a

@trueadm
Copy link
Contributor

trueadm commented Sep 12, 2024

Yeah I guess it would have to be any local reference, so maybe has_identifier or has_local?

@trueadm
Copy link
Contributor

trueadm commented Sep 12, 2024

So I tried this locally and have it working, unfortunately this test now fails:

<script>
	let min = 0;
	let max = 100;
	let number = 50;

	let value = 'hello';
</script>

<p>{Math.max(min, Math.min(max, number))}</p>
<p>{location.href}</p>

<Child prop={encodeURIComponent(value)} />

@paoloricciuti
Copy link
Member

So I tried this locally and have it working, unfortunately this test now fails:

<script>
	let min = 0;
	let max = 100;
	let number = 50;

	let value = 'hello';
</script>

<p>{Math.max(min, Math.min(max, number))}</p>
<p>{location.href}</p>

<Child prop={encodeURIComponent(value)} />

How is it failing?

@trueadm
Copy link
Contributor

trueadm commented Sep 12, 2024

So I tried this locally and have it working, unfortunately this test now fails:

<script>
	let min = 0;
	let max = 100;
	let number = 50;

	let value = 'hello';
</script>

<p>{Math.max(min, Math.min(max, number))}</p>
<p>{location.href}</p>

<Child prop={encodeURIComponent(value)} />

How is it failing?

My change sees value as a local identifier and makes the call expression dynamic. The purity test is a snapshot test on the output so now it's changed.

@paoloricciuti
Copy link
Member

My change sees value as a local identifier and makes the call expression dynamic.

So it seems we should just add a check to see if the identifier is also dynamic? Because if value was changed in this case we want to re-run it right?

@trueadm
Copy link
Contributor

trueadm commented Sep 12, 2024

My change sees value as a local identifier and makes the call expression dynamic.

So it seems we should just add a check to see if the identifier is also dynamic? Because if value was changed in this case we want to re-run it right?

We already have that, it's checked as part of state. However, that's not sufficient to fix the original issue:

<script>
	import { obj } from "./Data.svelte.js";
       ...
</script>

...
<Component lst={Object.values(obj)}/>

obj here isn't marked as dynamic.

@dummdidumm
Copy link
Member

dummdidumm commented Sep 13, 2024

So I tried this locally and have it working, unfortunately this test now fails:

Honestly, I think I'm fine with that. If you care about the microscopic advantage that you get from not having a template effect there, just don't write it out in the template directly and instead put it in a const in the script tag. This feels like an edge case either way - why would you write static variables in the script tag but then write a computation using them into the template?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants