在React中组成优于继承的优势

时间:2018-07-31 00:02:57

标签: reactjs inheritance composition

我很难用继承来代替React中的合成。我将基于React样式指南中不鼓励使用的继承,尝试说明所提出的问题和当前的解决方案。

首先,我在超级组件中定义通用状态和方法:

export default class SuperComponent extends Component {
    constructor(props) {
        super(props);
        this.state = commonState;
    }

    foo() {
        this.setState({a: 3});
    }

    render() {
        return (
            <div>
                {this.getContent()}
            </div>
        )
    }
}

然后,我使子组件具有更多的状态和方法。这些还应该可以访问超级组件的状态:

export default class SubComponent1 extends SuperComponent {
    constructor(props) {
        super(props);
        this.state = Object.assign(this.state, subComponent1State);
    }

    bar() {
        this.setState({b: 7});
    }

    getContent() {
        return (
            <div>
                I'm subcomponent 1
            </div>
        )
    }
}

export default class SubComponent2 extends SuperComponent {
    constructor(props) {
        super(props);
        this.state = Object.assign(this.state, subComponent2State);
    }

    bar() {
        this.setState({c: 1});
    }

    getContent() {
        return (
            <div>
                I'm subcomponent 2
            </div>
        )
    }
}

当我尝试将其转换为基于合成的方法时,会得到以下信息:

export default class SuperComponent extends Component {
    foo() {
        this.props.setStateMethod({a: 3});
    }

    render() {
        return (
            <div>
                <div>
                    {this.props.text}
                </div>
            </div>
        )
    }
}

export default class SubComponent1 extends Component {
    constructor(props) {
        super(props);
        this.state = Object.assign(commonState, subComponent1State);
    }

    bar() {
        this.setState({b: 7});
    }

    render() {
        return (
            <SuperComponent
                text={"I'm subcomponent 1"}
                setStateMethod={this.setState}
                subComponentState={this.state}
            />
        )
    }
}

export default class SubComponent2 extends Component {
    constructor(props) {
        super(props);
        this.state = Object.assign(commonState, subComponent2State);
    }

    bar() {
        this.setState({c: 1});
    }

    render() {
        return (
            <SuperComponent
                text={"I'm subcomponent 2"}
                setStateMethod={this.setState}
                subComponentState={this.state}
            />
        )
    }
}

这是将基于继承的解决方案转换为基于组合的解决方案的好方法吗?因为共同状态和区分状态被更好地分开,基于继承的继承不是更好吗?在基于组合的解决方案中,必须在初始化时在每个子组件中定义公共状态。

2 个答案:

答案 0 :(得分:1)

首先,您应该阅读React's Team response on the matter

代码非常模糊,因此我无法确定您的案子是否真的很特殊,但是我对此表示怀疑,所以让我解决普遍的共识:

在真空中,继承和组合同样有用。在某些语言或项目中,您将更喜欢从中继承一个通用类,而不是要导入和调用的十二个函数,但是在React中,主要是因为它是以组件为中心的方法,相反。

合成使React仅保持两件事:propsstate。您信任收到的内容,以可预测的方式采取行动,并在呈现的内容中同时反映propsstate。如果您需要稍微改变渲染的内容,则可以发送不同的props,甚至可以制作一个包装第一个用于特定用途的组件。该组件甚至可以自己拥有一些逻辑,然后将其传递给render props,以便您可以更精确地控制结果。如果需要使用此逻辑包装不同的组件,可以制作一个HOC并轻松包装任何组件。到那时,您可以制作数十个将不同事物返回到同一props的组件,并且可以在组件库之间切换以显示数据并进行处理。

尽管诸如Render props或HOC之类的东西看起来是不同的东西,但它们只是使用已存在的组件逻辑并以允许您重用代码并在React中仍然有意义的方式应用它的技术。这是一个好的解决方案吗?好吧,它行得通,但是最终您将陷入困境,并弄混了几乎相同的二十个概念。这就是为什么现在有一个proposal用于范式更改的新功能,该功能介于继承和合成之间。但是,在React的做事方式中,构图仍然没有意义。

归根结底,这是解决同一问题的另一种方式。合成在React上工作得更好,并在render中为您提供了更多控制权来混合和匹配您所需的内容。

同样,如果您有一个实际的例子,我可以使用不同的React技术为您提供更好的解释,但是对于您当前的方法,显然它没有好坏之分,因为它什么都不做。这需要花更多时间进行修改,而不仅仅是作出解释。

答案 1 :(得分:0)

在这种情况下,我认为您的解决方案不是基于合成的最佳解决方案。由于您希望子组件具有它们自己的状态(并且我假设它们将具有自己的逻辑),因此我将使用hoc,如下所示:

{
    "input": {
        "customClass": "input",
        "customModule": "abcInput",
        "customModuleProvider": "Whatever"
    },
    "button": {
        "customClass": "ViewController",
        "customModule": "Testing",
        "customModuleProvider": "target"
    }
}

var DOMParser = new (require('xmldom')).DOMParser({ normalizeTags: { default: false } }); var express = require("express"), bodyParser = require('body-parser'); require("body-parser-xml")(bodyParser); var xml2js = require('xml2js'); var builder = new xml2js.Builder({ standalone: { default: false } }); var app = express(); //Options of body-parser-xml module app.use(bodyParser.xml({ xmlParseOptions: { normalize: false, // Trim whitespace inside text nodes normalizeTags: false, // Transform tags to lowercase explicitArray: false // Only put nodes in array if >1 } })); //Post Method app.post('/users', function (req, res, body) { //Parsing Request.Body var document = DOMParser.parseFromString( builder.buildObject(req.body).toString() ); //Getting a list of elements whose name is being given var node = document.getElementsByTagName("TextView"); //Changing Tag Name of Specific Elements for (var i = 0; i < node.length; i++) { node[i].tagName = "com.mycompany.projectname.TextView"; } //Getting a list of elements whose name is being given var node = document.getElementsByTagName("com.example.usmanchattha.custom.TextView"); //Setting attributes for (var i = 0; i < node.length; i++) { node[i].setAttribute("android:id", "@+id / text2"); node[i].setAttribute("android:text", "Custom Android Font"); node[i].setAttribute("customfontdemo:chattha", "faizan"); node[i].setAttribute("android:padding", "12dp"); node[i].setAttribute("customfontdemo:fontName", "pipe_dream.ttf"); node[i].setAttribute("android:textSize", "32sp"); } //Responsing Updated Data res.send(document.toString()); }); app.listen(1000); 也将被包装:// hoc.js const withSuper = (SubComponent) => { class WithSuper extends React.Component { constructor(props) { super(props); this.foo = this.foo.bind(this); } foo() { // could also receive a value this.props.setState({ a: 3 }); } render() { // Passes to the SubComponent: // the state.a as a prop // function foo to modify state.a // the rest of the props that may be passed from above return ( <SubComponent a={this.state.a} foo={this.foo} {...this.props} /> ) } } return WithSuper; }; // SubComponent1.js class SubComponent1 extends Component { constructor(props) { super(props); this.state = { b: 0 }; // or anything else } bar() { this.setState({b: 7}); } render() { // to access the 'a' value use: this.props.a // to modify the 'a' value call: this.props.foo() return ( <div> I'm subcomponent 1 </div> ); } } export default withSuper(SubComponent1);,因此它也可以访问SubComponent2withSuper(SubComponent2)道具。

我认为这种解决方案更好,因为特殊的“超级”封装了a的行为,并且子组件只需要担心修改其特定状态。