69

One of the things I appreciate the most about Backbone.js is how simple and elegant inheritance works. I'm starting to get to grips with React, and can't really find anything in react that resembles this Backbone code

var Vehicle = Backbone.View.extend({
    methodA: function() { // ... }
    methodB: function() { // ... }
    methodC: function() { // ... }
});

var Airplane = Vehicle.extend({
    methodC: function() {
        // Overwrite methodC from super
    }
});

In react we have mixins, and using those we could get somewhat close to the above example if we went like this

var vehicleMethods = {
    methodA: function() { // ... }
    methodB: function() { // ... }
}

var Vehicle = React.createClass({
    mixins: [vehicleMethods]
    methodC: function() { 
        // Define method C for vehicle
    }
});

var Airplane = React.createClass({
    mixins: [vehicleMethods]
    methodC: function() {
        // Define method C again for airplane
    }
});

This is less repetitive than defining the same stuff over and over again, but it doesn't seem to be nearly as flexible as the Backbone way. For instance, I get an error if I try to redefine/overwrite a method that exists in one of my mixins. On top of that, the React.js way is more code for me to write.

There is some incredibly clever stuff in react, and it feels like this is more the case of me not getting how to properly do this, than it feels like a feature missing from React.

Any pointers are greatly appreciated.

Ahrengot
  • 1,579
  • 1
  • 17
  • 29
  • 3
    I find the following composition approach quite interesting. It's a move away from vanilla mixins: https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750 – ctrlplusb May 14 '15 at 09:33
  • It would be nice if whatever the React community decides to use for encapsulation/code reuse would be separate from the React framework. So it could be used outside of React, and so that there's more choice within React. Down with monoliths! – wprl Jun 02 '15 at 15:22

4 Answers4

61

To get something that resembles inheritance (actually composition as pointed out in comments), you can make an Airplane in your example wrap itself in a Vehicle. If you want to expose methods on Vehicle in the Airplane component, you can use a ref and connect them one-by-one. This is not exactly inheritance (it's actually composition), particularly because the this.refs.vehicle will not be accessible until after the component has been mounted.

var Vehicle = React.createClass({
    ...
});

var Airplane = React.createClass({
    methodA: function() {
      if (this.refs != null) return this.refs.vehicle.methodA();
    },
    ...
    render: function() {
        return (
            <Vehicle ref="vehicle">
                <h1>J/K I'm an airplane</h1>
            </Vehicle>
        );
    }
});

Also it's worth mention that in the React official documentation they prefer composition over inheritance:

So What About Inheritance? 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.

Props and composition give you all the flexibility you need to customize a component's look and behavior in an explicit and safe way. Remember that components may accept arbitrary props, including primitive values, React elements, or functions.

If you want to reuse non-UI functionality between components, we suggest extracting it into a separate JavaScript module. The components may import it and use that function, object, or a class, without extending it.

Another thing worth mention that using ES2015/ES6+ you can also spread the object props from Airplane component to the Vehicle component

render: function() {
    return (
        <Vehicle {...this.props}>
            <h1>J/K I'm an airplane</h1>
        </Vehicle>
    );
}
Fareed Alnamrouti
  • 30,771
  • 4
  • 85
  • 76
Ross Allen
  • 43,772
  • 14
  • 97
  • 95
  • 13
    Composition, like you have here, is generally the approach we suggest. I'll add, that when you do this, you might not even need the methods that you think you might need and can communicate more through props and rerendering. – Paul O'Shannessy Sep 08 '14 at 17:27
  • Thanks, @PaulO'Shannessy. I added a note that this is actually composition. – Ross Allen Sep 08 '14 at 20:21
  • @PaulO'Shannessy This is fine unless you want to wrap/extend methods like `#componentWillMount()`, which are called at a time when refs is not yet available. – Dmitry Minkovsky Dec 21 '15 at 17:43
  • Below somebody has suggested to use [reactto](https://github.com/jankuca/reacto). But I guess this answer is more appropriate as things in React should better be done the React way. Yes, the composition style of React makes here more sense. – Faisal Mq Jan 04 '16 at 13:48
  • 1
    I think the fact that this response used ellipses is exactly the elision that I see in the argument for composition. You're leaving out a huge amount of boilerplate. How is this such a better solution than the original case that it can be considered "correct"? – Adam May 25 '16 at 04:55
  • I think If my baseComponent is abstract, I don't want to render it. This case will not get the baseComponent's ref and can not access to baseComponent's methods. – Lin Du Aug 15 '16 at 08:39
5

If you use webpack+babel+react NPM package set up in your project, then in ES6 OOP implementation it should be as simple as this:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class BaseComponentClass extends Component {
  componentDidMount() {} 
  render() { return (<div>Base Component</div>) }
}
class InheritedComponentClass extends BaseComponentClass {
  // componentDidMount is inherited
  render() { return (<div>Inherited Component</div>) } // render is overridden 
}
ReactDOM.render(<InheritedComponentClass></InheritedComponentClass>, 
     document.getElementById('InheritedComponentClassHolder'));

Note: Here is a simple starter kit with such set up: https://github.com/alicoding/react-webpack-babel

Ashot
  • 399
  • 4
  • 7
  • 4
    Careful with this. You may encounter problems down the road since React recommends using composition over inheritance. I did that and I had to revert this approach mid project. It wasn't fun. – Pier Jan 02 '17 at 23:22
  • 4
    @Pier Very interesting, and [sure enough, the React docs say exactly that](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... **If you want to reuse non-UI functionality between components, we suggest extracting it into a separate JavaScript module.** The components may import it and use that function, object, or a class, without extending it.*" [emph mine] A `class` incompatible mindset, it appears. – ruffin Feb 02 '17 at 15:48
  • I've created a working demo to showcase this pattern and how the methods are overwritten! https://codesandbox.io/s/nice-robinson-8lk4y – sidonaldson Oct 14 '19 at 08:23
3

By leveraging ReactOO, you can easily use inheritance (Disclaimer: I'm the author of ReactOO):

(function () {

    // Class definitions. ReactOO requires class definition first just like you did in an OO way.

    window.Vehicle = window.ReactOO.ReactBase.extend({
        getReactDisplayName: function () {
            return 'Vehicle';
        },

        onReactRender: function (reactInstance) {
            var self = this;

            var text = self.methodC();

            return React.createElement('div', {}, text);
        },

        methodA: function () {
            console.log('Vehicle method A');
        },

        methodB: function () {
            console.log('Vehicle method B');
        },

        methodC: function () {
            return 'Vehicle method C';
        }
    });

    window.Airplane = window.Vehicle.extend({
        getReactDisplayName: function () {
            return 'Airplane';
        },

        methodC: function () {
            // Call this._super() to execute parent method.
            //this._super();
            return 'Airplane method C';
        }
    });

    var vehicle = new window.Vehicle();
    vehicle.render({}, '#vehicle');

    var airPlane = new window.Airplane();
    airPlane.render({}, '#airPlane');
})();
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>ReactOO Vehicle-Aireplane Example</title>
    <link rel="stylesheet" href="css/base.css" />
    <script src="scripts/react.js"></script>
    <script src="scripts/react-dom.js"></script>
    <script src="../src/reactoo.js"></script>
</head>
<body>
    <div id="vehicle">
    </div>
    <div id="airPlane">
    </div>
    <script src="scripts/vehicleAirplane.js">
    </script>
</body>
</html>
enter image description here
Broonix
  • 1,135
  • 2
  • 11
  • 26
Jim Ma
  • 709
  • 5
  • 15
-4

I think extending may be done with using jQuery.extend or similar

Base.jsx

module.exports = {
    methodA:function(){
        ...
    }
}

Child.jsx

var Base = require('./Base.jsx');

module.exports = React.createClass($.extend({},Base,{
    methodB:function(){
        ...
        this.methodA();
        ...
    },
    render:function(){
        return ...
    }
}));
pgregory
  • 2,093
  • 1
  • 12
  • 8