React擁有很強大的組合模型,我們建議使用組合來替代繼承來重利用組件之間的代碼。
在本章節中,我們將討論一些開發者經常觸及繼承的問題,並且我們該如何使用組合來解決這些問題。
組合
一些組件事先不知道它們的子組件。這種問題特別對於組件類似Sidebar或者Dialog這種通用的“盒子”。
我們建議像這樣的組件使用特殊的children屬性去直接傳遞子元素到它們的輸出里:
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> ); }
任何在<FancyBorder>JSX標簽的東西被作為一個children屬性傳遞給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 /> } /> ); }
像<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!" /> ); }
組合對於定義為類的組件同樣做得很好。
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和組合給了你所有的靈活性來讓你通過明確的和安全的方式自定義組件的樣子和行為。記住組件可能接受任意props,包括簡單的values,React elements或者函數。
如果你想要在組件之間復用與UI無關的功能,我們建議將它提取到一個單獨的js模塊里。這樣可以在不對組件進行擴展的前提下導入並使用該函數、對象或類。