十一、组合 VS 继承
React具有强大的组合模式,我们建议使用组合而不是继承来重用组件之间的代码。
在本节中,我们将考虑一些新的React常常遇到的开发继承的问题,并展示如何使用组合来解决它们。
有容乃大
一些组件提前不知道它们的子组件是什么的。 这对于像Sidebar
或Dialog
这样的代表通用“框”
的组件是特别常见的。
我们建议这些组件使用特殊的children属性
将子组件元素直接传递到他们中:
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
这让其他组件通过嵌套JSX传递任意到他们的children
中:
function WelcomeDialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
感谢参观鹏寰国际大厦
</p>
</FancyBorder>
);
}
最终的html结构为:
<div id="root">
<div data-reactroot="" class="FancyBorder FancyBorder-blue">
<h1 class="Dialog-title">Welcome</h1>
<p class="Dialog-message">感谢参观鹏寰国际大厦</p>
</div>
</div>
任何在<FancyBorder>
JSX标签内部的东西都会作为children props
被传递到FancyBorder
组件中。 由于FancyBorder
在<div>
中渲染{props.children}
,所以所传递的元素将显示在这个div
当中。
虽然这不常见,有时你可能需要在组件中有多个“窟窿”
。 在这种情况下,你可以提出自己的规范,而不是使用children
:
import React from 'react';
import ReactDOM from 'react-dom';
function Contacts() {
return <h1>tel:182012322**</h1>
}
function Chat() {
return <span>chat content</span>
}
function SplitPanel(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
)
}
function App() {
return (
<SplitPanel left={<Contacts />} right={<Chat />}/>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
像<Contacts />
和<Chat />
等React元素只是一个对象,所以你可以像任何其他数据一样把它们当做props去传递。
用的专业一点
有时我们认为某个组件是其他组件的“special cases”
。 例如,我们可能会说WelcomeDialog
是Dialog
的一个“special cases”
。
在React中,这也通过组合实现,其中更“特殊”
的组件渲染更“通用”
的组件并用props配置它:
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog(props) {
return (
<Dialog title="Welcome" message="欢迎参观鹏寰国际大厦" />
);
}
组合对于定义为类的组件同样有效:
import React from 'react';
import ReactDOM from 'react-dom';
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
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}!`);
}
}
ReactDOM.render(
<SignUpDialog />,
document.getElementById('root')
);
关于继承
在Facebook,使用了React在数千个组件,他们没有发现一个必须实现组件继承层次结构的用例。
props和组合给你所有的灵活性,你需要以一个明确和安全的方式自定义组件的外观和行为。 请记住,组件可以接受任意props,包括原始值,React元素或函数。
如果要在组件之间重用非UI功能,建议您将其提取到单独的JavaScript模块中。 组件可以导入它并使用该函数,对象或类,而不扩展它。