Skip to content

组合 vs 继承

React 有十分强大的组合模式。我们推荐使用组合而非继承来实现组件间的代码重用。

包含关系

有些组件无法提前知晓它们的子组件的具体内容。在Sidebar(侧边栏)和Dialog(对话框)等展示通用容器(box)的组件中特别容易遇到这种情况。 我们建议这些组件使用一个特殊的childrenprop来将他们的子组件传递到渲染结果中:

function FancyBorder(props){
    return (
        <div className={'FancyBorder FancyBorder-' + props.color}>
            {props.children}
        </div>
    )
}

这使得别的组件可以通过jsx嵌套,将任意组件作为子组件传递给他们。

function WelcomeDialog(){
    return (
        <FancyBorder color="blue">
            <h1 className="Dialog-title">welcome</h1>
            <p className="Dialog-messagge">
                Thank you for visiting our spacecraft!
            </p>
        </FancyBorder>
    )
}

<FancyBorder>jsx标签中的所有内容都会作为一个childrenprop传递给<FancyBorder>组件。因为FancyBorder{props.children}渲染到一个div中,被传递的这些子组件最终都会出现在输入结果中。

少数情况下,你可能需要在一个组件中预留出几个"洞"。这种情况下,我们可以不适用children,而自行约定:将所需内容传入props,并使用相应的prop。

function SplitPane (props) {
    return (
        <div className="SplitPane">
            <div className="SplitPane-left">{props.left}</div>
            <div className="SplitPane-right">{props.right}</div>
        </div>
    )
}

function App () {
    function Left () {
        return (
            <div>left</div>
        )
    }

    function Right () {
        return (
            <div>right</div>
        )
    }

    let Left1 = <div>left1</div>;
    let Right1 = <div>Right1</div>;
    return (
        <div>
            <SplitPane left={<Left  />} right={<Right  />}  />
            <SplitPane left={Left1} right={Right1}  />
        </div>

    )
}

<Left><Right> 和 Left1 、Right1 是React元素和jsx对象,本质上都是js的对象,所以可以把它们当做props,像其他数据一样传递。这种方法可能使你想到别的库中"槽"(slot)的概念,但是在React中没有"槽"这个概念的限制,你可以将任何东西作为props进行传递。

特例关系

有时候,我们会把一些组件看做其他组件的特殊实例,比如WelcomeDialog可以说是Dialog的特殊实例。 在React中,我们也可以通过组合来实现这一点。"特殊组件"可以通过props定制并渲染"一般"组件。

function Dialog (props) {
    return (
        <div color="blue">
            <h1 className="Dialog-title">{props.title}</h1>
            <p className="Dialog-messagge">
               {props.message}
            </p>
        </div>
    )
}


function WelcomeDialog () {
    return (
        <Dialog color="blue" title="welcome" message="thank you for visiting our home "  />
    )
}

组合也同样适用于以class形式定义的组件。

function Dialog (props) {
    return (
        <div color="blue">
            <h1 className="Dialog-title">{props.title}</h1>
            <p className="Dialog-messagge">
               {props.message}
            </p>
            {props.children}
        </div>
    )
}


class SignUpDialog extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            login:""
        }
    }
    handleChange= (e)=>{
        this.setState({
            login:e.target.value
        })
    }
    handleClick=()=>{
        alert(`welcome aboard,${this.state.login}`)
    }
    render(){
        return (
            <Dialog title="title" message="message">
                <input type="text" value={this.state.login} onChange={this.handleChange} />
                <button onClick={this.handleClick}>sign up</button>
            </Dialog>
        )
    }
}

那么继承呢?

在Facebook,我们在成百上千个组件使用React。我们并没有发现需要使用继承来构建组件层次的情况。 props和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式。注意组件可以接受任意的props,包括基本数据类型,React元素以及函数。 如果你想要在组件间复用非 UI 的功能,我们建议将其提取成一个单独的JavaScript模块,如函数、对象或者类。组件可以直接引入(import)而无需通过extend继承他们。