Open sorvell opened 10 months ago
Using this stateful
directive can unlock powerful behavior like this prototype, TLDR below:
const renderComments = (use, dataId) => {
const stages = use.state(() => {
const button = createRef();
const buttonClicked = interactive(button); // separate helper
return [
// initial
html`<button ref=${button}>Load Comments...</button>`,
// loading
new Promise(async (resolve) => {
await buttonClicked;
resolve(html`Loading...`);
}),
// comments
new Promise(async (resolve) => {
await buttonClicked;
await import('comments'); // import-mapped
const comments = await fetch(`commentsUrl/?id=${dataId}`);
resolve(html`<x-comments .comments=${comments}></x-comments>`);
})
].reverse();
});
return html`${until(...stages)}`;
}
// ...
html`${stateful(renderComments, 5)}`;
Would be great if Lit were to use @lit-labs/preact-signals (or native signals once widely available) to make stateful function components a thing
Motivation
Functions are the simplest unit of composition and the first tool a developer leverages to accomplish almost any task.
lit
empowers developers to write shareable interoperable code that creates rich interactive interfaces. Minimizing the distance and friction between starting with a function and ending with a great UI serves that goal.There are 2 main pieces of this puzzle: (1) describing rendering, (2) managing reactive state.
Describing rendering: Lit's
html
andcss
TTL syntaxes are excellent at this, and can easily be returned from a function.Managing reactive state:
LitElement
. If a dev started with a function, they must move to a class, and this is fine when it makes sense.effects
), but the lit signals package providesSignalWatcher
andwatch
to deal with this.This last issue is addressed below...
Example
Consider creating a simple counter:
In a LitElement, it looks like:
With a function using signals, this can be:
But this doesn't work because we need to get the
count
signal from somewhere.How
React solves this problem with hooks, but these have tradeoffs. Lit can make this more straightforward by leveraging the fact that all Lit templates are rendered into persistent "parts," and access to them is provided via the directives API.
So, all we need is a simple directive that can initialize state. Here's the updated counter example:
Then use it like this:
See working prototype.
The
stateful
directive provides astate
method which memoizes the result of its argument.References