Skip to content

Svelte 5: $state.link various bugs #12936

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
paoloricciuti opened this issue Aug 20, 2024 · 5 comments
Closed

Svelte 5: $state.link various bugs #12936

paoloricciuti opened this issue Aug 20, 2024 · 5 comments

Comments

@paoloricciuti
Copy link
Member

Describe the bug

I was playing around with $state.link to familiarise a bit with it and i think i've encountered 3 weirds behaviours that could be bugs.

The code is relatively simple

<script>
	let count = $state(0);

	let double = $state.link(count * 2, (doubled)=>{
		console.log('called');
		double = doubled;
	});
</script>

<button onclick={()=>{
	count++;
}}>
	{count}
</button>

<button onclick={()=>{
	double++;
}}>
	{double}
</button>

Weird things:

  1. if i update double once and then update count the updates are not reflected in the UI. This only happens if you pass the callback like so.
  2. the callback is always called twice when updating double, once with the old value and once with the new one.
  3. the code to update double from the callback looks like this
let double = $.source_link(() => $.get(count) * 2, (doubled) => {
	double($.proxy(doubled));
});

which sounds weird because the value is always proxified? I guess it's just impossible to check if it's a source or a proxy so we go with proxy?

Reproduction

REPL

Logs

No response

System Info

REPL

Severity

annoyance

@paoloricciuti
Copy link
Member Author

I started exploring this a bit...it feels a bit weird to me that we are calling the callback when arguments.length is equal to 0

return function (/** @type {any} */ value) {
if (arguments.length > 0) {
was_local = true;
set(local_source, value);
get(linked_derived);
return value;
}
var linked_value = get(linked_derived);
if (init) {
if (callback !== undefined) {
untrack(() => callback(linked_value));
return local_source.v;
}
} else {
init = true;
}
local_source.v = linked_value;
return linked_value;
};
}

shouldn't the callback be called when we set the value (and when the original value is set)? The two times the callback is called is once in the onclick and one in set_text...i suspect, adding a third display of the linked state could lead to a third call of the callback.

@Rich-Harris
Copy link
Member

Hmm. Yeah I had the same reaction to the placement of the callback but didn't interrogate it — seems our test coverage is insufficient. Looking into it.

Yes, whenever initialising or reassigning a binding with a value that could be proxifiable, we wrap it in $.proxy. If it turns out that it's not a proxifiable value, it doesn't do anything. In the case of a function parameter, it would be prohibitively difficult to determine that that it's unnecessary without some very significant changes to the compiler

@paoloricciuti
Copy link
Member Author

paoloricciuti commented Aug 20, 2024

Hmm. Yeah I had the same reaction to the placement of the callback but didn't interrogate it — seems our test coverage is insufficient. Looking into it.

It seems like moving that logic inside the derived instead then on read kinda fixes it but it introduce a couple of very big problems:

  1. now reassigning to derived value inside the callback leads to derived referencing itself and an error (or an infinite loop if the error was not there)
  2. that will inevitably lead to a derived with side effects which is no bono

@Rich-Harris
Copy link
Member

We just shouldn't be using a derived at all here, as far as I can tell. PR incoming

@Rich-Harris
Copy link
Member

$state.link no longer exists, so we can close this

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.

2 participants