Description
Ok, brain dump time.
We often get questions in the chatroom about why you can't use methods
in templates, and that sort of thing. And React people often look at code like this...
<Nested foo={bar}/>
<script>
import Nested from './Nested.html';
export default {
components: { Nested },
data: () => ({
bar: 1
})
};
</script>
...and ask 'why do you need to register the component? We don't have that problem because our apps are Just JavaScript™️'. And while there are good technical reasons for both of those things, in the context of the current design, the truth is they have a point.
I've occasionally wondered if there's a way to have the best of both worlds. It boils down to the template being able to access variables that are in scope in the <script>
:
<script>
import Nested from './Nested.html';
const bar = 1;
</script>
<Nested foo={bar}/>
The issue, of course, is that bar
is no longer reactive — there's no way to say 'this value has changed, please update the child component'.
Last week, the React team introduced hooks. If you haven't watched Sophie and Dan's talk, I do recommend it.
I've been slightly ambivalent about hooks — for the most part, they solve problems that are specific to React. But they do make me wonder if there's a way to solve the worst ergonomic drawbacks of Svelte's approach.
Here's a version of the example above using the useState
hook to make bar
reactive:
<script>
import { useState } from 'svelte/hooks';
import Nested from './Nested.html';
const [bar, setBar] = useState(1);
</script>
<Nested foo={bar}/>
<button on:click="setBar(bar + 1)">increment</button>
I had a go at rewriting a few more examples in this style to get a sense of what it would entail. I've been fairly pleasantly surprised; in many cases no changes are necessary (because the components only contain markup and possibly styles), and in the cases where changes are necessary, the code gets cleaner and more concise.
What I haven't done is figure out exactly what this would mean for the compiler. Svelte's whole deal is that it avoids doing as much work as possible, and there are two main components to that — avoiding touching the DOM, and not giving the user a place to accidentally do unnecessary and expensive computation. This means a) knowing which values could have changed, and b) not having a big ol' render function. This approach clearly makes that harder. Impossible? Not sure.
Things I'm thinking about, in no particular order:
setFoo(...); setBar(...)
instead ofset({ foo: 1, bar: 2 })
means you really need to batch the work up, which sort of forces us into an async paradigm. Maybe that's ok? Not sure- There'd need to be a way to access the component's props inside the
<script>
— at least inuseComputed
(React has useMemo for a similar purpose) and the equivalent ofuseEffect
- We've been encountering some tricky bugs recently around the order of
oncreate
,ondestroy
etc, particularly when there are bindings or immediateset(...)
calls. This feels like it could be a way to side-step those entirely - Need to consider the runtime complexity this would add
- Not sure how this could work with standalone components. Hooks work by maintaining a central registry, which doesn't immediately make sense for standalone things
- There's no obvious way to do inter-component bindings. Maybe
export { thingMyParentWants }
? - Need a way to fire events. They don't really exist in React-land but they're handy!
- What would this mean for SSR?
- There's certainly no need to slavishly follow the exact design of hooks — some folks are thinking about interop between different frameworks, but if it turns out not to make sense then there's no need to treat it as a constraint
This issue might alarm some of you; rest assured I'm not going to immediately rewrite Svelte just because Sunil goaded me on Twitter, and this is just the very beginning of this process of exploration. It may go nowhere. If we do end up using some of these ideas, we could probably leverage svelte-upgrade.
Key point to note: even if we do end up creating slightly more work for Svelte than it currently has to do (by making it less clear which values could have changed, etc) we'd still be in a better place than React, since a) we don't have the overhead of a virtual DOM, b) we have all the affordance of HTMLx, c) ...including <style>
.
I think it's at the very least worth considering — keeping Svelte feeling modern, and in line with evolving developer tastes, is an important consideration alongside the mission of making it easy to write small, fast apps (or making it difficult to write large slow ones).