-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
fix: invoke $state.link
callback at the correct time
#12938
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
Conversation
🦋 Changeset detectedLatest commit: 1534c1d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
One thing that occurs to me: we're not untracking the callback (either on |
It was untracked in the original implementation. However i think @trueadm was building all this to avoid setting state in an effect (also to future proof it when |
} | ||
render_effect(() => { | ||
if (ran) { | ||
callback(get_value()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will only invoke the callback the first time around rather then whenever the linked value changes right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No? It will run whenever get_value()
changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duh, mobile phone wrap around made me dirty...sprry
You absolutely can do this in userland, yes: #12545 (comment) In that comment I mention two drawbacks:
But both of these cut both ways. Maybe I want the effect to run the first time! And maybe I want to have control over whether it should re-run when non-untracked dependencies change. And the SSR issue could be solved with a simple userland helper of the kind that people have already built: let a = $state(0);
let b = $state();
watch(() => a, (a, initial) => {
b = a;
}); And here's something you can't do with $effect(() => {
some.deeply.nested.property = whatever;
});
We do want to discourage setting state in effects, for sure. But there are cases — the very cases where you'd use So... yeah. Honestly I'm back to wondering 'why the hell do we need this?'. The only reason I can think of right now is so that we can make non-bindable props readonly without sacrificing ergonomics too badly. |
I don't think this should be an effect at all. We want it to be read when accessing the linked state and the link has been updated. An effect will mean it runs whenever the link updates but this can lead to sync bugs. Which I can explain better tomorrow, but it's late here. |
Whether it uses the |
@Rich-Harris It's a grey area, but ultimately it's a reaction/computation. It's not an effect and it's not a derived. let count = $state(0);
let double = $state.link(count * 2, (doubled)=>{
double = doubled;
});
count++;
console.log(double); // 2 |
If that's an important characteristic then it definitely needs to be tested. A compromise option could be to remove the callback — if you need to do something when the state changes, use effects. I know I originally advocated for the callback, but now that we've shipped it I hate it. I had a niggling doubt when documenting the callback earlier, and I should have paid attention to it —there's no way to know, inside the callback, whether the linked state is newer than the original or vice versa, which feels like a pretty common requirement. In the docs I settled for a 'draw the rest of the owl' It still feels like it's probably unnecessary surface area even without the callback, but definitely to a lesser extent. |
See #12941 for the more complete fix. However, the big issue here is mutations as now the 2nd argument doesn't update when something inside it changes. So I feel like the second argument is problematic after all. |
This PR removes the second argument: #12942 |
Closing, as |
The current implementation involves some juggling that I don't really understand, and invokes the callback on read rather than just whenever the incoming value changes. It seems much simpler to just use an effect here.
Closes #12936
Before submitting the PR, please make sure you do the following
feat:
,fix:
,chore:
, ordocs:
.Tests and linting
pnpm test
and lint the project withpnpm lint