扩展React.js组件


68

关于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中,我们有mixin,如果使用mixin,我们可以像上面的例子那样

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中有一些非常聪明的东西,感觉更多的是我没有正确地做到这一点,而不是像React缺少的功能那样。

任何指针,不胜感激。


3
我发现以下组合方法非常有趣。这是远离香草混合物
ctrlplusb

如果React社区决定用于封装/代码重用的任何东西都与React框架分开,那将是很好的。因此它可以在React之外使用,因此React内有更多选择。倒下巨石!
wprl 2015年

Answers:


60

要获得类似于继承的内容(实际上是注释中指出的组成),可以Airplane在示例中将本身包装为Vehicle。如果要VehicleAirplane组件中公开方法,则可以使用ref并将它们一对一连接。这并不完全是继承(实际上是composition),尤其是因为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,但还没有发现建议创建组件继承层次结构的用例。

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

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

值得一提的另一件事是,使用ES2015 / ES6 +,您还可以将对象道具从Airplane组件传播到Vehicle组件

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

13
就像您在这里一样,合成通常是我们建议的方法。我要补充一点,当您执行此操作时,您甚至可能甚至不需要您认为需要的方法,并且可以通过道具和重新渲染进行更多的交流。
Paul O'Shannessy 2014年

谢谢@ PaulO'Shannessy。我添加了一条说明,它实际上是组成。
罗斯·艾伦

@ PaulO'Shannessy很好,除非您要包装/扩展类似的方法#componentWillMount(),这些方法在ref尚不可用时调用。
德米特里·明科夫斯基2015年

下面有人建议使用reactto。但是我想这个答案更合适,因为React中的事情最好以React方式完成。是的,React的合成样式在这里更有意义。
Faisal Mq

1
我认为这个响应使用椭圆的事实恰好是我在关于合成的论点中看到的省略。您将遗漏大量样板文件。与可以被视为“正确”的原始情况相比,这种解决方案又如何呢?
亚当

5

如果您使用项目中设置的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


4
小心点。由于React建议使用合成而不是继承,因此您可能会遇到问题。我这样做了,我不得不在项目中期恢复这种方法。不好玩
码头

4
@Pier非常有趣,而且可以肯定的是,React文档确切地说:“在Facebook,我们在成千上万个组件中使用React,我们还没有找到建议创建组件继承层次结构的用例...如果您要重复使用组件之间的非UI功能,我们建议提取它变成一个单独的JavaScript模块。这些部件可以导入并使用该函数,对象,或一个类,而不延伸它。 “[EMPH矿]一种class不相容的心态,它出现。
ruffin

我创建了一个有效的演示来展示这种模式以及如何覆盖这些方法!codesandbox.io/s/nice-robinson-8lk4y
sidonaldson

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 ...
    }
}));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.