Problem
借用react官網(wǎng)的例子,當(dāng)我們需要一個<Mouse>組件的時候,我們直接去實現(xiàn)它,我們初始的目標(biāo)是為了實現(xiàn)一個移動鼠標(biāo)同時可以顯示鼠標(biāo)位置,通過封裝可以實現(xiàn),如下:
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/* ...但我們?nèi)绾武秩?<p> 以外的東西? */}
<p>The current mouse position is ({this.state.x}, {this.state.y})</p>
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>移動鼠標(biāo)!</h1>
<Mouse />
</div>
);
}
}
但是實現(xiàn)了上面的<MouseTracker>組件后,需求又變了,我們需要實現(xiàn)的不只是顯示鼠標(biāo)位置了,同時需要在該位置上顯示一個<Cat>組件,這意味著我們需要再次卻封裝一個新的組件<MouseWithCat>,在這個組件里面包含了<Cat position={x: this.state.x, y: this.state.y} />,其目的就是為了<Cat>組件可以使用<Mouse>組件內(nèi)部的狀態(tài),但是這就需要我們必須去再次封裝一個新的組件,如果最后需要的有各式各樣的要求,針對每一個要求都去實現(xiàn)一個新的組件,而且這些組件都充斥著大量重復(fù)的代碼,這樣的工作是無意義的,且會帶來代碼的冗長。
// 這里我們實現(xiàn)了<MouseWithCat>,那如果我們還需要<MouseWithDog>呢?是不是還要再把這一套代碼再寫一遍?這明顯是重復(fù)的工作
class MouseWithCat extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/*
我們可以在這里換掉 <p> 的 <Cat> ......
但是接著我們需要創(chuàng)建一個單獨的 <MouseWithSomethingElse>
每次我們需要使用它時,<MouseWithCat> 是不是真的可以重復(fù)使用.
*/}
<Cat mouse={this.state} />
</div>
);
}
}
Solution (render props)
我們可以提供一個帶有函數(shù) prop 的 <Mouse> 組件,它能夠動態(tài)決定什么需要渲染的,而不是將 <Cat> 或是 <Dog> 硬編碼到 <Mouse> 組件里,并有效地改變它的渲染結(jié)果,這樣就避免了大量重復(fù)的代碼
// 首先修改<Mouse>組件的代碼,在最后render的內(nèi)容區(qū)域留一塊用來render將要傳進(jìn)來的內(nèi)容
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/*
Instead of providing a static representation of what <Mouse> renders,
use the `render` prop to dynamically determine what to render.
*/}
{this.props.render(this.state)}
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>移動鼠標(biāo)!</h1>
// 使用時就在<Mouse>組件上設(shè)一個函數(shù)形式的prop,當(dāng)然這個屬性名字可以隨便取,只要和前面對應(yīng)上就OK,特殊情況,把這個的名字設(shè)為children的時候,可以寫成這樣的形式
<Mouse>
{
mouse => (<Cat mouse={mouse} />)
}
</Mouse>
// 上下兩種情況對于prop是children的情況是等價的
<Mouse render={mouse => (<Cat mouse={mouse} />)}/>
</div>
);
}
}