React具有強大的組合模型,我們推薦使用組合來代替繼承,這樣可以在組件間重用代碼。
在本節中,我們將考慮幾個問題,遇到這些問題時,剛接觸React的開發者一般會考慮使用繼承,而我們將展示如何用組合來解決他們。
包含
一些組件提前不知道它的孩子。在諸如側欄和對話框這些通用“框”中尤為常見。
我們建議這種組件使用專門的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上試一試
<FancyBorder>
JSX標簽中的所有東西都會作為children
prop傳遞給FancyBorder
組件。FancyBorder
在<div>
中渲染{props.children}
,因此傳入的元素將出現在最終輸出中。
雖然這比較少見,但有時你還是會需要在組件中放置多個“槽位”。在這種情況下,你可以使用自己習慣來,來代替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上試一試
諸如<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上試一試
組合對于定義成類的組件同樣好使:
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,我們在數千個組件中使用React,尚未發現有什么組件,需要我們推薦你使用繼承去實現。
Props和組合提供了足夠的靈活度,使得你能夠以明確且安全的方法來自定義組件的外觀和行為。
如果你想在組件間復用非界面功能,我們建議將它抽取到單獨的JavaScript模塊。組件可以導入它,然后使用需要的函數,對象或類,而不必擴展(繼承)它。