引言
React拥有功能强大的组合模式,我们推荐使用组合而非继承来实现代码的复用。
在本章中,我们将要讨论几个React新手经常会遇到的关于继承的问题并展示我们如何使用组合解决它们。
包含关系
某些组件不能预先知道它的子元素是怎样的,这对于类似于SideBar或Dialog这类容器组件来说是十分常见的。
我们推荐在使用这类组件时使用特殊的children
prop直接将子元素渲染到输出。
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-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
在JSX标签<FancyBorder>
的任意值都会通过children
prop传递给FancyBorder
组件。所以由于FancyBorder
在<div>
中渲染了{props.children}
,所以被传递进来的元素最终都被渲染到页面上。
当然这种情况不太常见,但有时候组件中我们需要几个“洞”来传入对应prop。在这种情况下我们就需要自定义prop名而不是使用children了。
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
React元素<Contacts />
和<Chat />
只是对象,所以你可以像传递其他数据一样通过prop传递它们。这种方法可能会让你决定很想其他框架的“插槽”,但是在React中通过props传递的参数是没有限制的。
特例关系
有些时候我们需要某个组件称为区别于其他组件的特例,比如WelcomeDialog
就是Dialog
的一个特例。
在React中这也能够通过组合来实现。特殊组件通过渲染普通组件并加上适当的逻辑就可以达到想要的效果。
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}
组合也适用于class组件:
function Dialog(props) {
return (
<FancyBorder color="blue">
<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.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
那么继承呢?
我们使用了成千上万的组件来完成Facebook,但我们没有发现使用继承能有更好实现效果的例子。
props和组合让你自由,显式且安全地定义你的组件。请记得可以接收任意的props(基本类型数据,React元素或者方法)。
如果你想要在组件间复用非UI行的方法,那我们推荐把它提取成JavaScript模块,这样组件就可以import(引入)它而不是继承它。