React: Support inheritance

Created on 9 Jul 2017  Ā·  19Comments  Ā·  Source: facebook/react

I recall discussing about supporting inheritance for React components here, but unfortunately I can't find the discussion with github's search.

I know most of React's repository owners are functional programming lovers and I respect it. But there are other people who think while functional programming is good for many usecases, it's not best approach for every use case. Please don't ask me "what's your use case?" and try to handle it in your own way. I prefer to code in my own way and I do it fine, never had any problems with scalability/readability/maintainability/extendibility of my code over past 16 years. At least I never chose php for building web servers and then try to change php itself, even though my websites were much smaller than Facebook.

Another thing is that when you open source a great library like React, considering its huge user base, you should also take responsibility for all those users relation to your work. And those who respect people, don't force things to them. We expect our politicians to follow this rule, but we should also follow it when we're in a place that we make decisions that affect many others. We hate it when politicians use media to brainwash people and raise votes with populistic methods, I strongly believe React developers unconsciously and out of excitement did the same thing while they were advertising functional programming and things like Redux. So there's no wonder that if you make a poll today and "ask how many agree that we should support some basics of OO too?" most won't vote for it.

Another thing is that while a framework that forces a specific coding style may be a good framework, the one that doesn't force any coding style or avoids forcing as much as possible is much better.

Another thing is that while I agree with all the problems you described here and there about inheritance, there are usecases when none of those problems apply and natural thing to do is using inheritance.

I just can't believe all the trouble I went through past year just because React doesn't support inheritance. Many many times I reached to the point that I should stop doing frontend project till JS supports compiling python or C++ and browsers run it and people from those communities who still do their projects with OO come and write some good frameworks for Frontend developers.

I bet while reading this you're tempted to respond with "please just describe your usecase so that we tell you how to do it without inheritance and stop complaining" but believe me, you should avoid asking such questions. If I wanted to ask "how to do it with composition" I'd ask so. I know how to do it with composition but it just doesn't make sense as much as doing it with inheritance.

So I'm here to ask please support inheritance in React.

Just remember all the dirty full of parentheses code people were writing before you add support for classes in React. You said you did so just to improve code readability. Ok, now lets support inheritance to bring more readability to codes written with React.

Default values set in a class and inherited to its subclasses and still changeable by its subclasses can be done with passing props to a Component you compose in your render method. But sometimes it's just makes more sense to simply inherit, sometimes passing callback as props and taking care of it is just extra work for no gain.

Disclaimer: I like many many others do appreciate all the good things you did in React, my aggression is just a natural reaction to your passive aggression with forcing others to do things the way you prefer and all your "explain your problem, we'll tell you how to solve it our way" responses and all the troubles I had to adopt to your patterns, the patterns you force with your framework.

Btw I took time and wrote this just to improve React.js,

Most helpful comment

@sassanh I think the core point here (which we're straying from) is that React doesn't enforce inheritance or composition. We may _recommend_ using composition because we believe it's a generally useful starting point for abstraction in React, but there's nothing explicitly preventing you from using inheritance. As mentioned, the React API just asks that you extend React.Component and provide a render method. You can use whatever abstraction you like to build on top of that.

This issue tracker is meant for discussing bugs and feature requests. If you have specific problems with React preventing you from using inheritance we'd love to hear them! Otherwise, this isn't the best platform for discussing the pros and cons of composition and inheritance.

All 19 comments

Hi! As you asked, I won’t say ā€œdon’t use inheritanceā€, but we need to at least know which problems you’re running into. (It’s not obvious.)

Could you please clarify what exactly is not working for you? This seems a bit like a philosophical issue, but I’d really prefer to focus on specific problems or bugs you encounter.

As far as I know inheritance is fully supported by React. Or, rather, React is not opinionated about how you write classes, and should allow you to use inheritance, mixins, factories, your own userspace class model, a different language that compiles to JS and has its own class abstraction, etc.

As long as you inherit from React.Component and provide a render method, React should have no trouble recognizing it as a component. Even if you inherit ā€œdeeplyā€ and have your own hierarchy. Is that not the case in your experience?

Thanks!

@gaearon I tried again to find the earlier discussion but failed again, I wish github had a "participated" filter for issues.

Last time I discussed it here I remember there were problems inheriting from a class that inherits React.Component.

As far as I know inheritance is fully supported by React.

If currently React fully supports inheritance, which means if A inherits React.Component and B inherits A then all the React features including lifecycle methods, work on B then there's nothing to discuss.

I remember vaguely at the time I discussed it earlier the problem was that A's render method was called instead of Bs for instances of B in some circumstances. I can try it again if no one here is sure if it works or not, but that'll be pointless if you, the developers of React, are not willing to support it.

What's your policy about this issue? If I run into a problem using deep inheritance and report it here, are you willing to solve it? or will you respond with "use composition instead"?

Could you please clarify what exactly is not working for you? This seems a bit like a philosophical issue, but I’d really prefer to focus on specific problems or bugs you encounter.

Nothing "doesn't work" for me, there's no bug, but there is a problem, I'm willing to discuss the problem in details with you. The problem is about the complexity forced to my code because I can't use inheritance with React components.

It's related to immutability, redux, shallow comparison of props, reselect, performance and related things. I think these keywords brought an idea to your mind about the problem.

I can't find where but somewhere in http://redux.js.org/docs/ it is written that these problems can be handled "easily" with methods described "below". But the truth is these are not handled easily when you have a big complicated component (I think Facebook doesn't have such components, most complicated thing I saw in Facebook is its messenger, so I think when you say "we didn't need inheritance in Facebook codebase" you should also say: "our most complicated component is our messenger which by nature needs a functional design, an example of the complicated component I'm talking about is a text editor with features like MS-Word implemented with React for browsers, something like tinymce.)

Some of these problems need careful use of reselect and complicated combinations of connect, createSelector, passing callbacks, etc and taking care of each prop with reselect.

Now consider that you need multiple forms of this complicated component, with different behaviors. With composition it brings more callback passing, more attention is needed to take care you don't pass a big js data structure as the props while using composition (as you should pass data as props to the child element - the one you avoided to inherit from). While with inheritance you can simply provide the basic features in base class, and inherited classes won't need to take care of passing props that makes it unnecessarily re-render. To provide good performance sometimes even you need to forget about react automatic lifecycle methods invocation and instead of setState, store data in this.variable and force render when required.

Another situation I think it's better to avoid composition and use inheritance is when you're concerned about performance again and when you use composition you mount 2+ components instead of one, you render 2+ components instead of one, you shallow compare props for 2+ components instead of one, etc. With lots of compositions (instead of lots of inheritance) when many of components are rendered simultaneously I guess it matters. It's not currently my problem but I just thought about it.

To summarize: I'm saying that using composition for big/complicated components while trying to keep things performant, brings more complexity, take more time and reduces performance as it has overhead anyway. Software like macOS, Google Chrome, Pages, Linux Kernel aren't written with composition and functional programming (It may be best for Chrome to use functional programming for handling network packets, but not for its user interface, or plugin sandboxing, etc) React itself shouldn't be written with functional paradigms. React itself provides its api as classes and it discourages others to provide api as classes. A web application is not always a simple store or messenger or likes, sometimes your components are "engines" that you use in other components and they should provide good performance. There's a reason universities still teach OO: http://web.stanford.edu/class/archive/cs/cs108/cs108.1092/oldSite.shtml I understand most usecases for frontend developers fit well with paradigms you encourage people to use but there are usecases that don't fit with these paradigms. If React wants to be a general solution framework, it shouldn't force a paradigm. Just cause you created React to overcome problems you had with Facebook's messenger (which by nature needs a functional code) doesn't mean that all codes should be written as functional.

Qt's QML is a perfect reactive language, and by perfect I just mean "perfect". When I'm using Qt/QML I can write code without checking its documentation because everything there is just in harmony with intuition. It's designed in 2009, 4 years before React. It supports inheritance and it's just too useful there. I hope React.js can benefit from inheritance too. (ofc QML supports composition too.)

So I ask my question again:
What's your policy about this issue? If I run into a problem using deep inheritance and report it here, are you willing to solve it? or will you respond with "use composition instead"?
Is inheritance officially supported by you or you're just not opinionated about it?

Forgot to thank you for quick response šŸ˜„

I remember vaguely at the time I discussed it earlier the problem was that A's render method was called instead of Bs for instances of B in some circumstances. I can try it again if no one here is sure if it works or not, but that'll be pointless if you, the developers of React, are not willing to support it.

If it’s a bug, and it’s fixable, we’d like to know.

What's your policy about this issue? If I run into a problem using deep inheritance and report it here, are you willing to solve it? or will you respond with "use composition instead"?

It really depends on the issue and hard to discuss in abstract. We don’t have any specific policies. Bug reports compete with other bug reports based on perceived urgency and how hard it would be to fix. JavaScript is a very flexible language, and ā€œsupport for inheritanceā€ is a bit vague, covering everything from usual ES6 patterns (which I believe we support) to complicated metaprogramming (which might or might not work). I’m happy to try to provide you with likelihood of something being fixed for specific cases.

I can't find where but somewhere in http://redux.js.org/docs/

Redux is a very opinionated way of writing React code. Please don’t use Redux if it doesn’t fit your workflow. We don’t officially prescribe Redux anywhere, and we don’t even use it that much at Facebook.

I think Facebook doesn't have such components, most complicated thing I saw in Facebook <...>

Some of these problems need careful use of reselect and complicated combinations of connect, createSelector, passing callbacks, etc and taking care of each prop with reselect.

Again, I’m not sure how this is related to Facebook. Redux is not a Facebook project. Facebook mostly uses Flux for data management, although it does mostly lean towards immutable approaches. But there are diametrically opposite popular solutions in ecosystem like Mobx that embrace mutability. Perhaps you could give it a try instead?

That said your impression that Facebook’s most complicated components are in Messenger is not correct. Most complicated components (in my opinion) are in Ads Creation flow which includes interactive forms with very complex interconnected widgets for selecting ad targeting.

But again, they’re not using Redux. They’re using a combination of React component local state and Flux as far as I’m aware.

Is inheritance officially supported by you or you're just not opinionated about it?

It is likely that issues affecting only people using inheritance will have lower priority than issues affecting everyone. Not because we don’t like inheritance, but because fixing them just benefits much less people. That said, such bugs are not different from any other bugs. If they cause major distress and are fixable, we will try to fix them when we find the time.

I hope that answers your question. I can’t give a definitive answer there. We try to do our best whenever we can. If something is hard to fix and it’s unlikely it will be fixed, I’ll say so on the future issue reports.

Thanks for your positive response Dan. I just write some notes for future reference. I think these things will help someone who's trying to design his stack.

That said your impression that Facebook’s most complicated components are in Messenger is not correct. Most complicated components (in my opinion) are in Ads Creation flow which includes interactive forms with very complex interconnected widgets for selecting ad targeting.

I'm sure those components can be implemented without concerning about performance at all. By complicated components I meant those that are really cpu hungry by their nature.

Again, I’m not sure how this is related to Facebook.

This is how it's related to Facebook: here and there you write in docs and in your responses: "Facebook has huge codebase, we do x in Facebook like this, so we're sure anyone in whole universe should do the same we do as their codebase is smaller than Facebook." I think that conclusion is just wrong. Facebook doesn't have complicated (cpu hungry) components. Quoting from https://facebook.github.io/react/docs/composition-vs-inheritance.html

"At Facebook, we use React in thousands of components, and we haven't found any use cases where we would recommend creating component inheritance hierarchies."

Redux is a very opinionated way of writing React code. Please don’t use Redux if it doesn’t fit your workflow.

This is not what you said 1.5 year ago when I was building my stack. It all started with: https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367 and after reading that Medium post the message was clear for me. I guess it's not appropriate to discuss about Redux here. All I should say that is relevant is that I decided to keep using Redux as it was tightly bound to my huge codebase and removing it from my 80k lines of code would take around 20 days. I don't have any problems writing functional code except that JS is not REALLY a functional language (Python and C++ can pass functions as arguments too, doesn't mean they're functional).
The only problem is maintaining good performance in complicated components when using Redux in those components. I have solutions to keep my code clear, scalable and extensible while using Redux with my complicated components but it relies on using inheritance. The solutions you officially advertise would lead to overusing connect and reselect and complicated and not extensible code.

Generally I don't think a code written like a(b,b(),c(a,d,()=>{if (a) b() else c())) is easy to read. I think when you need a construct like (function(a) {this.x = 1 ...})() you should think twice and try to avoid it and use instantiating from a class instead. (function(a) {this.x = 1 ..., this.y()})() is not easy to reason about but

class A {
    constructor(x) {
        this.x = x;
    }

    y() {
        console.log(this.x);
    }
}

is readable and is "easy to reason about" ā„¢

So my conclusion here is to give inheritance another try and see if React has any problems with it. I'll report my experience here. I close this issue for now.

I think that conclusion is just wrong. Facebook doesn't have complicated (cpu hungry) components.

This perception is not correct in my experience. We have a lot of components where performance is critical (especially on React Native), and spend quite a bit of effort optimizing those. It’s getting a bit hard for me to talk to you because you seem to be very confident in what you’re saying but I don’t think you have enough context since you’re not working on Facebook products.

Redux is a very opinionated way of writing React code. Please don’t use Redux if it doesn’t fit your workflow.

This is not what you said 1.5 year ago when I was building my stack.

I don’t believe I ever said something like ā€œuse Redux everywhereā€. Please point me to where I did that.

I have solutions to keep my code clear, scalable and extensible while using Redux with my complicated components but it relies on using inheritance.

That’s cool if it works for you šŸ‘

Generally I don't think a code written like a(b,b(),c(a,d,()=>{if (a) b() else c())) is easy to read

I don't think this snippet is easy to read either. I think this discussion is turning into attacking strawman, which I’d like to avoid. I’m happy to discuss specific issues with React’s support for inheritance here, but I’d like to avoid debates about code style on the issue tracker.

I gave you 2 examples of why I think sometimes inheritance is better solution than composition, I think attacking straw-man started when instead of discussing those 2 cases, you emphasized on Facebook's components complexity.

To summarize it: I think composition reduces performance compared to inheritance specially in React framework. if you use bare React, it happens because you have to run whole React lifecycle on more components, you have to shallow compare props for more components, etc. In case of inheritance it's only one component (lets eliminate other factors and just focus on inheritance vs composition) when you compose it's more than one component. That's for bare React, if you're using Redux with ImmutableJS and reselect, etc. It'll bring even more overhead when you use composition. If the overhead for mounting, etc for a component was close to zero we could forget about it, but it's not. In my performance snapshot using react_perfit shows that mounting a small react component takes around ~30ms, lets assume it's 10ms in production environment, if you want to mount 500 inherited components it'll take 5 seconds. If you try to mount 500 composed components it'll take n x 5 seconds. (just for mounting.) Maybe you're tempted to respond with "don't mount 500 components, lazy load your components, etc." But why not? When I can achieve acceptable performance with inheritance why should I go with composition and add lots of complexity to my code to lazy load things. Or maybe even I want those 500 elements really at the same time. Lets not argue about milliseconds or number of components. I'll be glad to know your point of view about n in "n x 5" instead.

Anther issue with composition is the need to transfer methods from composing components to composed components to keep the api available on composed component. Even when you're not willing to change those methods. You can overcome this problems with mixins but that's just more complexity. Classes are designed to handle inheritance and I prefer to use them.

Another thing is complexity, when you're using Redux + ImmutableJS and reselect, with composition you need to spend more time and even write more code, more createSelectors and more connects to achieve the same.

Lets avoid attacking straw-man and discuss above 3 issues.

I think using functional paradigms for things that flow, like network packets, like data in user interface, like Facebook posts in timelines, or things like that is perfect and it's a must. But providing robust api and reusability is best done with help of object oriented programming. Both functional and object oriented can be used with declarative frameworks like QML and React. I think React will much better framework if it supports oo very well.

Disclaimer: Of course you can write reusable code with functional programming, specially when you're injecting a sense of objects with a declarative language like JSX, those declarative elements work like some point of reference among flow of functional structure which brings the idea of instances to functional programming. But sometimes when insisting to use functional approaches over object oriented you're avoiding simpler, easily readable code with some overheads for both the programmer and cpu.

Side note: When I talk about Facebook, I have a clear productive objection, which is "avoid generalizing the scope of problems you deal with Facebook's UI to scope of problems developers using React deal with" I strongly think avoiding this generalization leads to better React. I have no doubts Facebook (www.facebook.com) indeed is a big project, but I'm sure it doesn't cover all the different problems a web developer may deal with. This generalization should've been avoided even if React was developed in Google (and we know Google's web applications are more than Facebook's web applications by at least an order of magnitude.)

@sassanh I think the core point here (which we're straying from) is that React doesn't enforce inheritance or composition. We may _recommend_ using composition because we believe it's a generally useful starting point for abstraction in React, but there's nothing explicitly preventing you from using inheritance. As mentioned, the React API just asks that you extend React.Component and provide a render method. You can use whatever abstraction you like to build on top of that.

This issue tracker is meant for discussing bugs and feature requests. If you have specific problems with React preventing you from using inheritance we'd love to hear them! Otherwise, this isn't the best platform for discussing the pros and cons of composition and inheritance.

but there's nothing explicitly preventing you from using inheritance. As mentioned,

If you have specific problems with React preventing you from using inheritance we'd love to hear them!

That's cool šŸ‘

So my conclusion here is to give inheritance another try and see if React has any problems with it. I'll report my experience here. I close this issue for now.

@sassanh any luck with fixing react? I'd like to see this feature too)

@TrejGun What exactly doesnt work for you?

This was the error I was dealing with around a year ago: https://www.webpackbin.com/bins/-KqZieQ-0HbptXHgilb2

It happens with React 15.2.1. Seems like it's fixed in React 15.3.0. But I clearly remember when I asked somewhere (can't remember in a stackoverflow comment or in a github thread) a React repository owner answered: We just support inheritance as limited as inheriting directly from React.Component and only because it's cleaner code than old createComponent and when I asked them to kindly support deeper inheritance at least for the same purpose of "cleaner code" if nothing else they tried to convince me that I should use functional programming.

I'm glad ~one year later React is mature enough that it

doesn't enforce inheritance or composition.

I’m sorry if there was a confusing reply from somebody in the team.

I’m not sure if it was mine, but I can assure you the stance of React team on this has not changed since React first came out. We are not recommending inheritance (especially to beginners who don’t yet understand composition is possible and solves common use cases) but we also don’t (and in fact technically can’t) prevent you from using it. Inheritance is a language feature—there’s no way React as a library could forbid you from using it.

The bin you’re linking to errors because React.PureComponent does not exist in 15.2.0. It was added in 15.3.0 as you can see in the changelog. The error in question (TypeError: Super expression must either be null or a function, not undefined) does not come from React. If you look at where it gets thrown, it is inside the code Babel creates for your component:

screen shot 2017-08-02 at 23 38 47

This happens because you are inheriting from React.PureComponent but it doesn’t exist. (As I mentioned, it was added in a later version.) So "super expression" (the thing you inherit from) is undefined.

I’m sorry if somebody on the team interpreted this specific message as a bug in React. Did you provide the same details with a bin that time?

I’m not sure if it was mine

It wasn't you unless your avatar at the time had green elements in it as his github avatar had green elements.

We are not recommending inheritance (especially to beginners who don’t yet understand composition is possible and solves common use cases)

It's cool you try to introduce composition to beginners, but I think you should separate "React as a js library", and "teaching things based on your taste to beginners" in different projects. This way people who are using React as a library won't be annoyed by hints they're not willing to get.
My point is even though I totally respect your desire to teach and I appreciate your work on the library, I think it's better to avoid combining these two together. Provide a library not opinionated and provide teaching materials separately, don't use your framework as a tool to boost your opinion. It's better for community too.

but we also don’t (and in fact technically can’t) prevent you from using it. Inheritance is a language feature—there’s no way React as a library could forbid you from using it.

I'm glad you don't, but in fact technically you can prevent inheritance and it's as simple as checking the super class of the component and if it's not directly inheriting from React.Component you can render null. So prevention is "technically" easily possible. Though I never said you prevent it by intention, another way to prevent which is not so intentionally is by not fixing bugs related to deeper inheritance.

The bin you’re linking to errors because React.PureComponent

Yeah I just wrote a simple inheritance example and went back through react versions to find in which version it raises an error. Sorry for misleading bin. Now I investigated my project's git history and found the issue. The issue is that (it's not solved yet) if you inherit from a class that inherits from React.Component its displayname is the same as its super class. It shows itself in react-devtools. At the moment this problem combined with the conversation I talked about made me conclude that deep inheritance isn't supported as a first class citizen in React.js and it may not receive the performance optimizations you were planning to do for arrow functions and later optimizations in fiber specially when I heard that inheritance is there solely for cleaner code. Over time it was changed in my mind as React.js have serious problems with rendering deep inherited classes.

it's as simple as checking the super class of the component

Good point, I actually forgot one could do that. Thanks for correcting!

The issue is that (it's not solved yet) if you inherit from a class that inherits from React.Component its displayname is the same as its super class. It shows itself in react-devtools

I remember that one, yes. So, here is where it gets a bit fuzzy. We do support inheritance in React itself. But it is sometimes harder (or just doesn’t seem worth it) in projects around it. I think if you can find a good fix for this, we’d be happy to take it, but it won’t be a high priority since this is just a less used case.

What about performance in React itself? I remember reading somewhere that you have different optimizations for arrow functions and classes inherited from React.Component so I think React doesn't deal with different type of components the same and internally different codes handles components created with different method (arrow function, inherited classes). Does using deeply inherited classes introduce any performance issues that you're aware of?

I don't think we have any optimizations based on whether you inherit from Component or not. I'm not aware of any optimizations related to arrow functions either. The only thing that gets a special optimization is inheriting from PureComponent. But that works deeply too (it just puts a special field flag on the prototype that React reads).

@TrySound
Last time I checked it was problem with hot reloading
Something like

The following modules couldn't be hot updated: (Full reload needed)

but apparently this is not an issue now

Yeah good point, at the time I had problems with livereactload and deeply inherited components too.

Was this page helpful?
0 / 5 - 0 ratings