Расширение компонентов React.js

Одна из вещей, которые я ценю больше всего в Backbone.js, - это то, как работает простое и элегантное наследование. Я начинаю разбираться с React и не могу найти ничего в реакции, похожее на этот код Backbone

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

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

В ответ мы имеем mixins, и используя те, которые мы могли бы приблизиться к приведенному выше примеру, если бы мы пошли следующим образом

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
    }
});

Это менее повторяемо, чем определение одного и того же материала снова и снова, но оно, похоже, не так гибко, как метод Backbone. Например, я получаю сообщение об ошибке, если попытаюсь переопределить/перезаписать метод, который существует в одном из моих миксинов. Кроме того, способ React.js - это больше кода для меня.

В реагировании есть некоторые невероятно умные вещи, и мне кажется, что это больше похоже на то, что я не получаю, как правильно это делать, чем это чувствуется как функция, отсутствующая в React.

Приветствуются любые указатели.

Ответ 1

Чтобы получить что-то похожее на наследование (на самом деле, как указано в комментариях), вы можете сделать Airplane в вашем примере, обернуть себя в Vehicle. Если вы хотите выставить методы в Vehicle в компоненте Airplane, вы можете использовать ref и подключать их по очереди. Это не совсем наследование (это фактически композиция), особенно потому, что this.refs.vehicle не будет доступен до тех пор, пока компонент не будет установлен.

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>
        );
    }
});

Также стоит упомянуть, что в React официальная документация они предпочитают состав над наследованием:

Итак, что насчет наследования? В Facebook мы используем React в тысячах компонентов, и мы не нашли каких-либо вариантов использования, рекомендуется создавать иерархии наследования компонентов.

Реквизит и композиция дают вам всю гибкость, необходимую для настраивать внешний вид и поведение компонентов явным и безопасным способом. Помните, что компоненты могут принимать произвольные реквизиты, включая примитивные значения, элементы реакции или функции.

Если вы хотите повторно использовать функции, отличные от UI, между компонентами, мы предложите извлечь его в отдельный модуль JavaScript. компоненты могут импортировать его и использовать эту функцию, объект или класс, без его расширения.

Еще одна вещь, которую стоит упомянуть, что с помощью ES2015/ES6 + вы также можете распространять реквизиты объекта из Airplane в компонент Vehicle

render: function() {
    return (
        <Vehicle {...this.props}>
            <h1>J/K I'm an airplane</h1>
        </Vehicle>
    );
}

Ответ 2

Если вы используете webpack + babel + отредактируете пакет NPM, настроенный в вашем проекте, то в реализации ES6 OOP это должно быть так просто:

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'));

Примечание. Вот простой стартовый набор с такой настройкой: https://github.com/alicoding/react-webpack-babel

Ответ 3

Используя ReactOO, вы можете легко использовать наследование ( Отказ от ответственности: Я автор 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>

Ответ 4

Я думаю, что расширение может быть выполнено с помощью jQuery.extend или подобного

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 ...
    }
}));