# 組合和繼承
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>
);
}
~~~
在 CodePen 中[打開查看](http://codepen.io/gaearon/pen/ozqNOV?editors=0010)。
在 `<FancyBorder>` JSX 標簽中的任何內容被傳遞到 FancyBorder 組件作為一個 `children` prop。由于 FancyBorder 渲染 {props.children} 到一個 `<div>`,傳遞的元素會出現在最終的輸出中。
然而這并不常見,有時你可能在一個組件中需要多個 “洞”。這個情況你可以使用自己的約定而不是使用 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 />
} />
);
}
~~~
在 CodePen 中[打開查看](http://codepen.io/gaearon/pen/gwZOJp?editors=0010)。
如 `<Contacts />` 和 `<Chat />` 等 React 元素都是對象,所以你可以傳遞它們作為 props,就像其它數據一樣。
## 特殊化
有時我們思考組件為相對于其它組件的“特殊情況”。例如,我們可能說一個 WelcomeDialog 是 Dialog 的一個特殊用例。
在 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() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}
~~~
在 CodePen 中[打開查看](http://codepen.io/gaearon/pen/kkEaOZ?editors=0010)。
組合工作同樣需要組件定義為一個類組件:
~~~
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}!`);
}
}
~~~
在 CodePen 中[打開查看](http://codepen.io/gaearon/pen/gwZbYa?editors=0010)。
## 那么繼承呢?
在 Facebook 中,我們在千萬的組件中使用 React,我們沒有發現任何用例值得推薦創建繼承層級的組件。
Props 和 組合已經足夠靈活來自定義一個組件的外觀和行為,并且是明確而安全的方式。記住,組件可以接受任意 props,包括原始值、React 元素,或者函數。
如果你希望在組件間復用非 UI 功能,我們建議提取它為單獨的 JavaScript 模塊。組件可以導入它并使用這個函數
對象或者類,而不是擴展繼承它。