组合 vs 继承
React 有十分强大的组合模式。我们推荐使用组合而非继承来实现组件间的代码重用。
包含关系
有写组件无法提前知晓它们的子组件的具体内容。在Sidebar
(侧边栏)和Dialog
(对话框)等展示通用容器(box)的组件中特别容易遇到这种情况。
我们们建议这些组件使用一个特殊的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-messagge">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
)
}
<FancyBorder>
jsx标签中的所有内容都会作为一个children
prop传递给<FancyBorder>
组件。因为FancyBorder
将{props.children}
渲染到一个div
中,被传递的这些子组件最终都会出现在输入结果中。
少数情况下,你可能需要在一个组件中中预留出几个“洞”。这种情况下,我们可以不适用children
,而自行约定:将所需内容传入props,并使用相应的prop。
function SplitPane (props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">{props.left}</div>
<div className="SplitPane-right">{props.right}</div>
</div>
)
}
function App () {
function Left () {
return (
<div>left</div>
)
}
function Right () {
return (
<div>right</div>
)
}
let Left1 = <div>left1</div>;
let Right1 = <div>Right1</div>;
return (
<div>
<SplitPane left={<Left />} right={<Right />} />
<SplitPane left={Left1} right={Right1} />
</div>
)
}
、 和 Left1 、Right1 是React元素和jsx对象,本质上都是js的对象,所以可以把它们当做props,像其他数据一样传递。这种方法可能使你想起别的库中“槽”(slot)的概念,但是在React中没有“槽”这个概念的限制,你可以将任何东西座位props进行传递。
特例关系
有时候,我们会把一些组件看做其他组件的椰树实例,比如WelcomeDialog可以说是Dialog的特殊实例。
在React中,我们也可以通过组合来实现这一点。“特殊组件”可以通过props定制并渲染“一般”组件。
function Dialog (props) {
return (
<div color="blue">
<h1 className="Dialog-title">{props.title}</h1>
<p className="Dialog-messagge">
{props.message}
</p>
</div>
)
}
function WelcomeDialog () {
return (
<Dialog color="blue" title="welcome" message="thank you for visiting our home " />
)
}
组合也同样使用于以class形式定义的组件。
function Dialog (props) {
return (
<div color="blue">
<h1 className="Dialog-title">{props.title}</h1>
<p className="Dialog-messagge">
{props.message}
</p>
{props.children}
</div>
)
}
class SignUpDialog extends React.Component{
constructor(props){
super(props)
this.state = {
login:""
}
}
handleChange= (e)=>{
this.setState({
login:e.target.value
})
}
handleClick=()=>{
alert(`welcome aboard,${this.state.login}`)
}
render(){
return (
<Dialog title="title" message="message">
<input type="text" value={this.state.login} onChange={this.handleChange}/>
<button onClick={this.handleClick}>sign up</button>
</Dialog>
)
}
}
那么继承呢?
在Facebook,我们在成百上千个组件使用React。我们并没有发现需要使用继承来构建组件层次的情况。
props和组合为你提供了清洗而安全地定制组件外观和行为的灵活方式。注意组件可以接受任意的props,包括寄出数据类型,React元素以及函数。
如果你想要在组件间复用非ui的功能,我们建议将其提取成一个单独的JavaScript模块,如函数、对象或者类。组件可以直接映入(import)而无需通过extend继承他们。