扩展 React.js 组件

IT技术 javascript inheritance backbone.js reactjs
2021-03-11 20:02:26

我最欣赏 Backbone.js 的一件事是简单而优雅的继承是如何工作的。我开始掌握 React,但在 React 中找不到任何类似于此 Backbone 代码的内容

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

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

React 中我们有 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 方式那么灵活。例如,如果我尝试重新定义/覆盖存在于我的一个 mixin 中的方法,我会收到错误消息。最重要的是,React.js 方式是我编写的更多代码。

React 中有一些非常聪明的东西,感觉这更像是我不知道如何正确地做到这一点,而不是感觉像是 React 缺少的一个功能。

任何指针都非常感谢。

4个回答

要获得类似于继承的东西(实际上是评论中指出的组合),您可以Airplane在示例中将自己包装在Vehicle. 如果要VehicleAirplane组件中公开方法,可以使用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,我们还没有发现任何我们建议创建组件继承层次结构的用例。

props和组合为您提供了以明确和安全的方式自定义组件外观和行为所需的所有灵活性。请记住,组件可以接受任意props,包括原始值、React 元素或函数。

如果您想在组件之间重用非 UI 功能,我们建议将其提取到单独的 JavaScript module中。组件可以导入它并使用该函数、对象或类,而无需扩展它。

另外值得一提的是,使用 ES2015/ES6+ 还可以将对象 props 从Airplane组件传播Vehicle组件

render: function() {
    return (
        <Vehicle {...this.props}>
            <h1>J/K I'm an airplane</h1>
        </Vehicle>
    );
}
我认为这个响应使用省略号的事实正是我在组合论证中看到的省略。你遗漏了大量的样板文件。这如何比原始案例更好的解决方案可以被认为是“正确的”?
2021-04-21 20:02:26
@PaulO'Shannessy 这很好,除非您想包装/扩展像 那样的方法#componentWillMount(),这些方法在 refs 尚不可用时被调用。
2021-05-02 20:02:26
像您在这里看到的那样,组合通常是我们建议的方法。我要补充的是,当您这样做时,您甚至可能不需要您认为可能需要的方法,并且可以通过道具和重新渲染进行更多交流。
2021-05-11 20:02:26
谢谢,@PaulO'Shannessy。我添加了一个注释,这实际上是组合。
2021-05-11 20:02:26
下面有人建议使用reactto但我想这个答案更合适,因为 React 中的事情最好以 React 的方式完成。是的,React 的组合风格在这里更有意义。
2021-05-16 20:02:26

如果你在项目中使用 webpack+babel+react 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

我已经创建了一个工作演示来展示这种模式以及这些方法是如何被覆盖的!codeandbox.io/s/nice-robinson-8lk4y
2021-04-28 20:02:26
@Pier 非常有趣,果然,React 文档准确地说:“在 Facebook,我们在数以千计的组件中使用 React,而且我们还没有找到任何我们建议创建组件继承层次结构的用例......如果你想要在组件之间重用非 UI 功能,我们建议将其提取到一个单独的 JavaScript module中。组件可以导入它并使用该函数、对象或类,而无需扩展它。 ” [emph 我的] 一种class不兼容的心态,它出现。
2021-05-01 20:02:26
小心这一点。由于 React 建议使用组合而不是继承,因此您可能会遇到问题。我这样做了,我不得不在项目中期恢复这种方法。这不好玩。
2021-05-07 20:02:26

通过利用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>
在此处输入图片说明

我认为扩展可以通过使用jQuery.extend或类似的方式完成

基础.jsx

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

儿童.jsx

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

module.exports = React.createClass($.extend({},Base,{
    methodB:function(){
        ...
        this.methodA();
        ...
    },
    render:function(){
        return ...
    }
}));