React 入门教程之九 -- composition 模块化 和 inheritance 继承
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 等。
标签:无