-
Notifications
You must be signed in to change notification settings - Fork 81
Reactive assignments #4
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
Co-Authored-By: Rich-Harris <[email protected]>
I like that! Or maybe just Update: to heck with that, jQuery is dead long live the $ store! |
@Rich-Harris, actually, I'm not sure we understand each other correctly. To prevent any misunderstandings, I try to describe controversial points more deeply:
I'd argue, for example, I need to make some side-effect when some prop or internal state values are changed. If I we'll be able to use onprops(() => {
...
});
....
onprops(() => {
...
}); There's not any difference between that and events usage. But events win in this case because we automatically have an opportunity to listen to them outside of the component.
Events are that the source will want that they were. Event producer able to put any payload to the event. So, seems to me, that callback become redundant in the case when we anyway need to have external events corresponding with
I'm not sure about it. Seems, the compiler can't be absolutely sure that external event const comp = new Component({ ... });
...
comp.on('update', () => {
/* external code wants to know when an internal state changed */
}) So, you need to include this lifecycle stuff anyway. I believe it's not a problem because component needs to have minimal and predictable external api.
Actually, this case is really unusual. I don't think that we could to think up many such cases. I'm not sure I will ever want to have a dynamic name of the tag. On the other side really ugly <svelte:meta>
<!-- should become ? -->
<meta ... > I believe compiler options could be and maybe even should be static. <script>
/* very clean and habitually */
export default {
immutable: true,
tag: 'my-tag'
};
</script>
<!-- really ugly -->
<svelte:meta immutalbe={true} tag="my-tag"> Besides, if we'll ever need to have some static function for configuration or any other purpose, I'll be able to use it with export default {
preload() {
/* hi sapper */
}
};
I agree, but my point is - if we'll consider removing component bindings, I think element's bindings are also should be removed, because it have much less meaning than component's bindings.
I believe this is the most clean solution for spreading props.
Perhaps, we don't need to make that expression reactive? Seems compiler will not be able to make reactive all imports, right? <script>
import { some_object, some_variable } from '3rd_party_lib';
let foo = 0; // 'this is internal state and will be reactive'
</script>
<div>
<button on:click={e => foo += 1}>Increment</button>
<!-- how we can make this reactive? -->
<button on:click={e => some_variable += 1}>Increment</button>
</div> My point is, in v3 we'll not able to make all imports are reactive, but we'll give an access to any import in our templates. So, maybe we can just use api of these external modules as is? <script>
import { some_object, some_variable } from '3rd_party_lib';
let foo = 0; // 'this is internal state and will be reactive'
</script>
<div>
<button on:click={e => foo += 1}>Increment</button>
<!-- we can just use an api of external libs ? -->
<button on:click={e => some_variable.set(some_variable + 1)}>Increment</button>
</div>
Not really :) It's more looks like Custom Elements emulation. I'm talking about when Right now Svelte component is a constructor which returns an object, but how about to return actual DOM node with extended api? This node can be considered by us as a certain analog of shadowRoot. We can wrap all our component's templates to one additional node, extend that node with Svelte-specific things and return it as imperative version of component for external usage. This approach will gives us full power of DOM API right over svelte component and also will increase compliance with the standard Custom Elements where components has shadowRoot node. const comp = new Component({ ... });
comp.querySelector('.some');
comp.addEventListener('click', () => {});
document.body.appendChild(comp); You can check how it works in Angular here. |
typo: |
export let foo; | ||
|
||
beforeUpdate(() => { | ||
// this callback runs whenever props change |
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.
I believe this also runs when reactive assignments are made, so I think "props" here may be confusing since I understand props to only be those values that are exported.
// this callback runs whenever props change | |
// this callback runs whenever props or state change |
text/0001-reactive-assignments.md
Outdated
|
||
## Unresolved questions | ||
|
||
The major TODO is how `Store` should evolve in this new world. |
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.
I believe this is resolved now by #5
|
||
### Dependency tracking | ||
|
||
In Svelte 2, 'computed properties' use compile-time dependency tracking to derive values from state, avoiding recomputation when dependencies haven't changed. These computed properties are 'push-based' rather than 'pull-based', which can result in unnecessary work: |
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.
Is this section still valid with the reference implementation of #8? I think #8 is a much cleaner solution, I wonder what would be the expected behavior of function calls in html. I think the most sensible approach would be to re-run the function only when reactive arguments change.
<script>
import { fn } from './functions';
</script>
<ul>
<li>{fn()} - static, called on initial render</li>
<li>{fn(a, b)} - dynamic, called whenever a or b change</li>
</ul>
b7023ee
to
cebc90d
Compare
Thanks for writing this up. I haven't read all the comments, but I read the RFC, and I have a few concerns:
let foo
function recompute() {
performance.mark('start')
foo = doExpensiveFooCalculation()
performance.measure('total', 'start')
} Again, some of this is hard to predict because of the compiler magic. Overall, I think this proposal is very ambitious and interesting, and it's definitely going to push Svelte in a wild new direction. I'm a little wary of introducing such a huge new change, though, as it will take a long time to upgrade a large app like Pinafore. Also, I for one actually enjoy the |
Re: the linting issue, yeah this change does make it less useful to just use e.g. |
This is tricky to quantify without actually converting an app. The smallest apps will get larger, because there's a bit more in the way of scheduling code etc, but my gut tells me that larger apps will get smaller, for various reasons (more things can be hoisted out of In terms of performance, it's not so much that it'll become faster or slower across the board, but rather that performance will be significantly improved in the worst cases. In Svelte 2 it was much too easy to end up in a situation where you were doing interleaved DOM reads and writes, because of the synchronous updates — this is no longer the case. Longer term we should be able to finesse this further (for example being smart about when transitions are triggered, causing DOM measurements).
It's true that there'll be some finagling involved depending on your setup. As @Conduitry says this is solvable. In Svelte 2 I always have a problem that isn't solvable AFAICT — referring to built-in-methods inside hooks and custom methods causes red squiggles, because VSCode is smart about figuring out object shapes etc, but (justifiably) not smart enough to know the idiosyncracies of a Svelte
Agree. I think I need to update this RFC with the latest thinking — basically, I came to the conclusion that tracking which values are read during a function invocation isn't going to work, and would result in unpredictable performance characteristics. Instead, we're pursuing an idea that's more similar to Svelte 2's
There are two parts to this — the The update itself is in the framework's control. There are a couple of ways you could profile that — by putting I definitely recognise that this is going to be a pain to upgrade, and I regret that. I do think it's necessary though — a lot of issues are extremely hard to solve in Svelte 2 but will become easy (or irrelevant) in Svelte 3, and it allows us to jettison a lot of compiler complexity which will let us move faster. There'll be a lot less for newcomers to learn, and components will be easier to write. Do keep the feedback coming, particularly if there are elements of the 3 RFCs that haven't been explained as well as they could be. |
Perhaps we should remove the bit about function call dependency tracking, link to the reactive declaration RFC, and merge this, as it's fairly settled now. |
Agree re removal — have updated it. I think we're supposed to announce a 'final comment period' before merging, if we're respecting the RFC process... I guess we should do that if the three v3 RFCs are in good shape |
Actually, before we do that, we should probably do a yak-sweep. There are a few instances of 🐃 remaining, and I'm not sure how many are still relevant. |
A proposal for simpler, smaller components with a new approach to reactivity.
View formatted RFC
See also the original issue sveltejs/svelte#1826
Due to an unfortunate incident at the commit factory, the original PR for this RFC was merged prematurely. Earlier comments can be read there