-
Notifications
You must be signed in to change notification settings - Fork 790
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
Error On Any Inheritance #1060
Comments
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:
but we have
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! |
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 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. |
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 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 |
Hello there, while we wait for a solution for this... This may not be the perfect solution, since MDN recommends against using 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 Another alternative, if you are just wanting to reuse some specific methods, you may opt for doing something like:
or simply @Component({...})
export class Form {
// ...
componentDidLoad = BaseComponent.prototype.componentDidLoad;
// ...
} |
@fsodano Thanks, works, but as said here above, pretty .. 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 |
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
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 |
@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. 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. |
Hi Guys! 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.
And also you should avoid arrow functions in the component |
@manucorporat @adamdbradley You mentioned using utility functions in this issue, is there any examples or docs on how this can be accomplished? #936 |
I have a question regarding this subject. 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.
|
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 |
I guess component composition is a proven pattern and a more flexible alternative to inheritance. |
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. |
Some have asked how to implement the composition solution that was mentioned. Here is what I have done. Consider a set of date-picking controls. One is DatePicker and a similar one is DayOfYearPicker, and we have DateRangePicker. They have similar needs from a UI standpoint, much can be shared as far as presenting a date, opening a dropdown, navigating a calendar, representing selection, etc. I made a contract which the Stencil Date Components must fulfill:
Now I made this Stencil component for the DatePicker, it implements this IDateComponent interface. I wrote the date component entire logic here. And then I transferred it to a separate class which is not a Stencil component. I left only the Stencil decorators and lifecycle hooks in the Stencil component. Everything else I call into this new "base class". In Stencil component ctor I instantiate base class. So I called it "base class" this is just a name. It is not inherited but it treats it the same way you would "super". This is what we mean by "composition", the base is just a class composed with the component.
You see this is just a flyweight wrapper now, a facade around the real class, where I do not have Stencil's inheritance limitations. DateComponent, now is just what used to be in the AceDateComponent. But from there once again I moved all of that logic to a DateComponentBase, an abstract class. This is where all of the display logic ended up. Just put it in a new .tsx file, you can import anything you want here and e.g. render jsx markup, it's not going to bother inheritance.
Now in my base component I can touch the Stencil component using the injected
Just write "this.component" instead of "this." And I have a few classes who extend that. These are the ones the components are composed with:
Now I make regular Stencil component for AceDayOfYearPicker and AceDateRangePicker. They have a duplicate of the wrapper above I made for AceDatePicker. This is where we're forced to duplicate some code, but that has been minimized to only the props, and lifecycle hooks, anything that needs a Stencil decorator, needs a stub here, which will always be 1 line that calls into the composed "base" class. The only difference is which base do they new in their ctor. AceDayOfYearPicker is composed with a "base" of DayOfYearComponent, and AceDateRangePicker is composed with DateRangeComponent, and these bases inherit from the abstract DateComponentBase. Hope this can help. ✌️ |
Stencil version:
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.
The text was updated successfully, but these errors were encountered: