react 有一套完善了 composition 构造模型,推荐使用 composition 代替 inheritance 来在 components 之间复用代码。下面介绍在开发的具体场景中常常需要用到 inheritance 的地方如何用 composition 解决。

containment 包含

一些 components 并不直接知道他们的 children 具体是什么。在 Sidebar 或 Dialog 中可以体现,它们只是一个 box 容器,他的内容可能是变化的。

这一类的 components 推荐直接使用 children props 来直接表示 parent 传递给他们的 elements:

const FancyBorder = props => {
    return (
        <div className={`FancyBorder FancyBorder-${props.color}`}>
            {props.children}
        </div>
    );
}

props.children 表示所有在调用此 component 时放在其元素中的内容。

然后我们创建一个 component 来调用上面的 FancyBorder:

const WelcomeDialog = () => {
    return (
        <FancyBorder color='red'>
            <h1 className='Dialog-title'>Welcome</h1>
            <p className='Dialog-message'>
                thank you for check this page.
            </p>
        </FancyBorder>
    )
}

任何在 FancyBorder 标签中的内容都会作为 children prop 传入 FancyBorder 中,然后通过 props.children 进行渲染。

但通常情况下我们的 compenent 可能会有多个 “入口”,此时我们就需要定义自己的 convention 声明来替代 children:

const React = require('react')
const ReactDOM = require('react-dom')

const SplitPane = (props) => {
    return (
        <div className='SplitPane'>
            <div className='Splitpane-left'>
                {props.left}
            </div>
            <div className='Splitpane-right'>
                {props.right}
            </div>
        </div>
    )
}

const Contacts = () => {
    return (
        <div>
            <h1>marco</h1>
        </div>
    )
}

const Chat = () => {
    return (
        <div>
            <p>this is a test</p>
        </div>
    )
}

const App = () => {
    return (
        <SplitPane
            left={
                <Contacts />
            }
            right={
                <Chat />
            }
        />
    )
}

ReactDOM.render(
    <App />,
    document.getElementById('root')
);

可以看到,我们可以将 component 像其他属性一样传递,这里 Contacts 和 Chat 就作为 left 和 right 的数据传递给 SplitPane 使用。

specialization 特殊化

一些场景下,我们会将某个 component 看作另一个 component 的特殊情况,例如上面示例中的 WelcomeDialog 可以看做是 Dialog 的特殊情况。在 react 中我们通常通过给一个 generic 泛用的 component 配置 props 的方式构成另一个 special component:

const React = require('react')
const ReactDOM = require('react-dom')

const FancyBorder = props => {
    return (
        <div className={`FancyBorder FancyBorder-${props.color}`}>
            {props.children}
        </div>
    );
}

const Dialog = props => {
    return (
        <FancyBorder color='red'>
            <h1 className='Dialog-title'>
                {props.title}
            </h1>
            <p className='Dialog-message'>
                {props.message}
            </p>
        </FancyBorder>
    )
}
const WelcomeDialog = () => {
    return (
        <Dialog 
            title='Welcome'
            message='welcome to this party'
        />
    )
}

ReactDOM.render(
    <WelcomeDialog />,
    document.getElementById('root')
);

我们也可以通过 class 的方式定义 component:

const React = require('react')
const ReactDOM = require('react-dom')

const FancyBorder = props => {
    return (
        <div className={`FancyBorder FancyBorder-${props.color}`}>
            {props.children}
        </div>
    );
}

const Dialog = props => {
    return (
        <FancyBorder color='red'>
            <h1 className='Dialog-title'>
                {props.title}
            </h1>
            <p className='Dialog-message'>
                {props.message}
            </p>
            {props.children}
        </FancyBorder>
    )
}

class SignUpDialog extends React.Component {
    constructor(props) {
        super(props);
        this.handleChangle = this.handleChangle.bind(this);
        this.handleSignup = this.handleSignup.bind(this);
        this.state = {login: ''};
    }

    handleChangle(e) {
        this.setState({login: e.target.value});
    }

    handleSignup() {
        alert(`welcome guys, ${this.state.login}`)
    }

    render () {
        return (
            <Dialog
                title='Sport game'
                message='welcome to this game'
            >
                <input value={this.state.login} onChange={this.handleChangle} />
                <button onClick={this.handleSignup}>Sign me up</button>
            </Dialog>
        )
    }
}

ReactDOM.render(
    <SignUpDialog />,
    document.getElementById('root')
);

这里我们使用了自定义 props 和 children props 来构建了 SignUpDialog component,根据实际场景灵活使用。

props 和 composition 提供了灵活性来自定义一个 component 的样式/行为,且更加安全和精确。需要注意的是 component 可能会接收到 arbitrary 抽象 props,包括原始二进制数据/elements/functions 等。

标签: none

添加新评论