Stencil version:
@stencil/[email protected]
I'm submitting a:
[X] bug report
The change in 0.12 to introduce a compiler error when a Stencil component inherits from _any_ class is causing us a lot of problems. Most of our components inherit from non-Stencil base classes that define common properties (not @Props) and methods.
While I can understand that supporting inheritance between components is hard, blocking all inheritance seems like a poor solution. Can the error be changed to stop you inheriting only from other classes that are _also_ annotated with @Component and leave open the option of inheriting from non-component classes? At the very least can we get some kind of flag or option to disable this check? As it stands we can't move to 0.12.
Currently we do not allow for any inheritance of Stencil Components. We have talked about possibly allowing for mixins in the future. But this will need to be outlined in our future roadmap.
I am tagging this as a feature request. Thanks!
The lack of inheritance is a bit problematic from a productivity point of view.
Copying and pasting the same set of code, for example for click events, to different components, and then readjusting when necessary is time consuming, and then you have to keep track of changes and modify all components after one modification.
But well, there seems to be strong reasons against it.
Perhaps there could be more patterns examples for composition (until with get some cool stuff) ?
I'm not sure this is a feature request? Maybe I didn't explain what I meant properly. I understand that the following doesn't work:
@Component(...)
class Animal {}
@Component(...)
class Dog extends Animal {}
but we have
class Animal {}
@Component(...)
class Dog extends Animal {}
Our base class then handles our generic service interactions (in our case with the redux store).
This did work prior to 0.12, when the compiler error was introduced.
@lupiter I think your use case is valid and we should allow for it.
@lupiter could you provide the base class that you wanted to extend. We are trying to identify solutions to the problem. I just want to have better insight. Thank you!
@jthoms1
Here you go. It should remind you of stencil-redux but because we don't have a root component (we're making a component collection) we setup the redux store with a special <sw-store> component. getSubscription is related to configuring a redux middleware.
import { ActionType, AppState, Store, StoreStates, Subscription } from 'global/state';
import { extractFromGlobal } from 'global/store';
export abstract class SWComponent {
public abstract store: Store;
public abstract storeManager: any;
public abstract applyState(state: AppState): object;
public abstract storeState: StoreStates;
protected subscriptionId: string;
public linkStore(): void {
if (!this.store) {
this.store = extractFromGlobal();
}
this.store.mapStateToProps(this, (state: AppState) => this.applyState(state));
if (!this.subscriptionId) {
this.subscriptionId = this.store.getId();
}
this.store.dispatch({ type: ActionType.UPSERT, payload: [this.getSubscription()] });
this.store.mapDispatchToProps(this, this.getSavingProperties());
}
protected getSavingProperties(): object {
return {};
}
protected getSubscription(): Subscription {
return new Subscription(this.subscriptionId, []);
}
public willLoad(): void {
if (this.storeManager && this.storeManager.componentOnReady) {
this.storeManager
.componentOnReady()
.then(() => {
this.linkStore();
})
.catch((reason: string) => {
console.error(reason);
if (window.name !== 'nodejs') {
throw new Error('Couldn\'t find a <sw-store> element, May be misconfigured.');
}
this.linkStore();
});
} else if (window.name !== 'nodejs') {
throw new Error('Couldn\'t find a <sw-store> element, May be misconfigured.');
}
}
}
I've just updated to .13* and it's broken what was previous working in a basic extends case. Not doing anything fancy - just trying to provide a handful of utility methods in a parent class so I'm not repeating code and creating a maintenance nightmare. Please allow this once again. Thanks.
@lupiter I think your use case is valid and we should allow for it.
I do not understand why inheriting from an imported class is considered valid, but the inheritance of a component isn't. IMHO this should be exactly the other way around. Handling dependencies on a component basis seems to be much more convenient than including / shipping global dependencies from external files.
Although there had been plenty of feature requests for component inheritance, there hasn't been a clear statement. The most promissing issue has been closed due to inactivity while waiting for an answer from @jthoms1 , others just refer to the closed one.
The ability to inherit from a component is a key feature to me and it seems like others base their decision of working with stenciljs on this aswell. Will stenciljs ever support inheritance of components? If so, does a roadmap exist?
_My use case is a "window" component that comes in different variations: <win-alert/>, <win-confirm/>, <win-prompt/>... they all have to inherit the base functionality from <win-base/> which comes with the window container, a title bar, close / minimize / maximize buttons and some global APIs for handling events, a taskbar etc._
Thanks.
@jthoms1 are you still looking for practical examples where inheritance would be a good solution/welcomed?
if so, I could provide one soonish, I'm duplicating code in 3-4 components of the same project right now and I miss having the ability of having an abstract class ;)
Any update on that one? We have to duplicate code because of this error...
We need this as well: we want to use the <ion-back-button /> component, but slightly adjust its behavior when clicked.
The only way I see to go about this would be to copy/paste large portions of the ionic component into my own. Unfortunately, that component depends on things like the createColorClasses() function, which can only come from @ionic/utils/theme.
Our specific use-case actually arose because we're trying to include a stencil PWA within a larger Ionic app that targets native devices. The <ion-anchor> and other ionic links don't know which router to interact with, which causes issues, so we need to handle that click behavior/routing within the PWA separately to avoid conflicts.
Hello there, while we wait for a solution for this...
This may not be the perfect solution, since MDN recommends against using Object.setPrototypeOf(), but at least it provides an alternative.
Since classes are just syntactical sugar (i.e. JS it still remains prototype based), we can leverage this and change the prototype of a stencil component, to be the prototype of our base component.
import {Component, Element} from "@stencil/core";
import {BaseComponent} from "../../base-component";
@Component({
tag: 'ti-form',
styleUrl: 'form.css'
})
export class Form {
@Element() el: HTMLFormElement;
render() {
return (
<form>
<slot/>
</form>
)
}
}
Object.setPrototypeOf(Form.prototype, BaseComponent.prototype);
This has its limitations (for instance, you can't use super).
Another alternative, if you are just wanting to reuse some specific methods, you may opt for doing something like:
Form.prototype.componentDidLoad = BaseComponent.prototype.componentDidLoad;
or simply
@Component({...})
export class Form {
// ...
componentDidLoad = BaseComponent.prototype.componentDidLoad;
// ...
}
@fsodano Thank you for this input. As helpfull it is, it's still pretty ugly.
Do you have guys (@jthoms1) any timeline for this feature/bug?
@fsodano Thanks, works, but as said here above, pretty ..
And again, if any timelines for inheritance?
Do builder with lots drag-drop elements, and have manually add same drag-drop methods to many components, looked badly, so use this crutches, and started to await
Inheriting from a base class without any component decorators should really be allowed imo.
Bump. It's been 6 months since @jthoms1 agreed above that this is valid and switched it from feature to bug.
I looked into creating a PR for this, but I've found some seemingly bizarre (and seemingly unnecessary) behaviour that's getting in the way: If an exported class is not decorated with @Component
I disabled both of the above, and everything seems to work just fine. But the behaviour is so unexpected, it makes me think there's a reason (but there's no comments about it in code, nor in the PR that introduced them). I'm not too familiar with the source of Stencil, so perhaps it's obvious to someone more familiar?
Also, why is double-decorating not allowed? I would think it perfectly fine, and subsequent decorations would just stomp previous ones where they collide. I tried doing that (after disabling those checks), and it seems to work fine.
@adamdbradley could you provide any context?
@manucorporat does that label means it's fixed already, and if so in what release? I couldn't find any PR related to this.
@nielsboogaard have a look at the branch core-refractor, also these breaking changes might be relevant. For more details visit stencil's slack.
Is there any release date for stencil one?
Do you know maybe how to get to the stencil's slack?

@FRSgit regarding Slack: https://stencil-worldwide.herokuapp.com
Stencil 1.0 will not allow any inheritance of component classes, cuz stencil might need to apply transformation of the base class. In addition, allowing inherintance opens the world to a completely new world of problems, since the compiler will most likely not be able to static analyze the prototype chain at compiler time.
Stencil might emit components that works in workers, on extend directly from HTMLElement, or even angular components directly, it needs to be able to change the base class.
We have got a lot of feedback about how to build components and reuse code, we have found that there are good pattern that can replace the need for inheritance. We will extend docs with more information about code sharing between components soon!
Okay, the only @peterpeterparker ah, sorry, didn't saw that on readme page, my bad.
@manucorporat That's a pity, but understandable. Are there any plans on supporting extending native elements though? Im' talking about components defined like here.
Supporting this approach would give a possibility to write better (semantic-wise) html code, so as a result also a real boost for all SEO-connected usecases.
@manucorporat Did those docs about code sharing between components get written? Would love a link if so!
I'm specifically wondering how I can take an existing Ionic element and modify it to have some custom behaviors, without having to copy/paste and essentially fork the entire element. If this is not possible, I likely will not be able to adopt Stencil for my project.
Hi Guys!
I'm was working on a large scale internal Design System and developed a pattern for creating components that extend functionality. It is based on Polymer's behavior mixin pattern, but it's cleaner.
Suggestions please!
https://gist.github.com/menosprezzi/9d4de98960d343a4283e268073402b6c
In this example we have a layout component that controls a navdrawer that closes if focus change.
I'll revise this pattern and write an Medium article soon as possible explaining the concept
thanks!
Is there any best practice to achieve the same behavior as inheritance provides? It's a pretty big problem for me, I don't want to repeat the same code in all my classes...
@manucorporat Hi! Do you have more information about how to share code between Stencil components? Thanks!
I found workaround to share code between Stencil components. But in any case it inconvenient.
You can use use decorator from typescript-mix library and also duplicate all props and state to the new component.
import { use } from 'typescript-mix';
import { Component, Prop, State } from '@stencil/core';
import { ComponentToOverride } from '../path';
export interface OverrideComponent extends ComponentToOverride {}
@Component({
tag: 'override-component',
styleUrl: 'style.scss',
shadow: true,
})
export class OverrideComponent {
@use(ComponentToOverride) this: ComponentToOverride;
@State() stateValue: string;
@Prop() name: string;
}
And also you should avoid arrow functions in the component
@manucorporat
Any update yet on how to achieve code sharing between components? This is a huge non-starter for our team, and IMO an essential feature for something like Stencil aiming to be the best way to build design systems. Simply not scalable without it.
@adamdbradley You mentioned using utility functions in this issue, is there any examples or docs on how this can be accomplished? https://github.com/ionic-team/stencil/issues/936
I have a question regarding this subject.
I want that all my library components have a theme property or for example RTL support or something that will be present in all components.
How can i use inheritance like class CarCompoent extends VehicleComponent?
I sow @jthoms1 saying that we should use composition but how can i do that?
using a component and then a
But in this case i can't style the slot deeply.
Can someone help me with this issue?
So what's the solution for reusable code if we can't use inheritance?
I'm just going to leave this here.
Basically it says :
Just because Adam Abramov doesn't like the inheritance, doesn't mean we all have to follow the same pattern, if in the end we're going to follow the same React pattern, can you tell me what I want to use Stencilfor? --> Nextj.js
I guess component composition is a proven pattern and a more flexible alternative to inheritance.
Just because React and Stencil follow a similar approach when it comes to reusing certain functionality doesn't mean they do the same thing. But I'm not here to argue, I just wanted to provide you with a possible solution for reusing functionality without inheritance.
Have a nice one !馃檪
Composition alone isn't enough when you want to be able to share / pass through a common set of props without having to duplicate those props for every single component. I use composition to build my design systems with styled-system, its definitely the way to go, but its not feasible if I have to repeat the same set of 20-30 common styling props on every component (color, margin, padding, etc)
Would be cool to have inheritance implemented. I have a certain use-case where the code would be so much cleaner and easier to maintain.
Most helpful comment
I do not understand why inheriting from an imported class is considered valid, but the inheritance of a component isn't. IMHO this should be exactly the other way around. Handling dependencies on a component basis seems to be much more convenient than including / shipping global dependencies from external files.
Although there had been plenty of feature requests for component inheritance, there hasn't been a clear statement. The most promissing issue has been closed due to inactivity while waiting for an answer from @jthoms1 , others just refer to the closed one.
The ability to inherit from a component is a key feature to me and it seems like others base their decision of working with stenciljs on this aswell. Will stenciljs ever support inheritance of components? If so, does a roadmap exist?
_My use case is a "window" component that comes in different variations: <win-alert/>, <win-confirm/>, <win-prompt/>... they all have to inherit the base functionality from <win-base/> which comes with the window container, a title bar, close / minimize / maximize buttons and some global APIs for handling events, a taskbar etc._
Thanks.