-
Notifications
You must be signed in to change notification settings - Fork 818
Description
Prerequisites
- I have read the Contributing Guidelines.
- I agree to follow the Code of Conduct.
- I have searched for existing issues that already include this feature request, without success.
Describe the Feature Request
Lit has added support for what it calls "Reactive controllers" in its latest major release. These are plain JS classes which conform to a known interface, and are able to hook into a component's lifecycle.
They are generic enough that they can be used/adapted to most/all frameworks, as they are not dependent on Lit at all (aside from interface definition, but that could be externalised). Therefore stencil could add support for these by implementing ReactiveControllerHost interface.
This could be considered a community standard, and would allow for sharing of functionality between stencil and lit (and any other frameworks that opt-into the standard), perhaps creating an ecosystem for pieces of reusable behaviour
Controllers are a mechanism for extracting both behaviour and state from a component in a reusable way.
Relevant links:
- https://lit.dev/docs/composition/controllers/
- https://lit.dev/docs/api/controllers/
- https://github.com/lit/lit/blob/f8ee010bc515e4bb319e98408d38ef3d971cc08b/packages/reactive-element/src/reactive-controller.ts#L53
- [labs] Reactive controller adapters for other frameworks lit/lit#1682
Describe the Use Case
There are currently no ways "official" ways to share functionality/logic/behaviour between components. At best you can do some manipulation of the component instance in a helper function, but this always feels fragile.
Reactive controllers offer a way to express a "has-a" relationship, and are composable. You can extract common behaviour and state for reuse between components and even frameworks.
Describe Preferred Solution
Stencil's base component class should implement ReactiveControllerHost. This could be as simple as:
class StencilBaseClass implements ReactiveControllerHost {
controllers = new Set<ReactiveController>();
addController(controller: ReactiveController) {
this.controllers.add(controller);
}
removeController(controller: ReactiveController) {
this.controllers.delete(controller);
}
requestUpdate() {
forceUpdate(this);
}
connectedCallback() {
this.controllers.forEach((controller) => controller.hostConnected());
}
disconnectedCallback() {
this.controllers.forEach((controller) => controller.hostDisconnected());
}
componentWillUpdate() {
this.controllers.forEach((controller) => controller.hostUpdate());
}
componentDidUpdate() {
this.controllers.forEach((controller) => controller.hostUpdated());
}
}Just this would be enough to satisfy the support of reactive controllers.
Describe Alternatives
On slack, @snaptopixel described an alternative approach based around functional components: https://gist.github.com/snaptopixel/9dd86455a5791b65e9a0c0e576c097b6
However, this is entirely custom and stencil-specific, so would not be interoperable and is not framework agnostic.
Related Code
See the following gist: https://gist.github.com/WickyNilliams/79ee85ea370506ac6b16de1920f48e5e
This demonstrates the use of a number of custom controllers integrated into an example stencil component.
The MouseController is lifted verbatim from the Lit docs.
Perhaps, more interesting is that is uses the Task controller published by the lit team, as an example of how functionality can be shared across frameworks!
Additional Information
No response