简介
一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术
可以分享一个组件封装到其他需要相同state组件的状态或行为。
示例
我们要监听某一个组件中的鼠标位置。
我们可以使用监听事件 拿到事件对象并获取其中的位置信息。
class MousePosition extends Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
const {x,y} = this.state;
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
<p>当前的鼠标位置是 ({x}, {y})</p>
</div>
);
}
}
初步需求
要求添加一个logo,并跟随鼠标进行移动。如下
解决: 对 logo 使用绝对定位,并将鼠标位置赋给 logo。
class MousePosition extends Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
const {x,y} = this.state;
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
<p>当前的鼠标位置是 ({x}, {y})</p>
<img style={{width:50,height:50, //<--- +
position:'absolute', //<--- +
left:{x} ,top:{y}}} //<--- +
src="/icon.gif" alt="icon" /> //<--- +
</div>
);
}
}
增加需求
我们需要在另一个地方使用不同的 logo 达到相同的效果;
方案一:我们可以使用将 img的src 提取为一个变量 imgsrc
class MousePosition extends Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
const {x,y} = this.state;
const {imgsrc} = this.props; //<------ ++++
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
<p>当前的鼠标位置是 ({x}, {y})</p>
<img style={{width:50,height:50,
position:'absolute',
left : x ,top : y }}
src={imgsrc} alt="icon" /> //<--- +
</div>
);
}
}
然后通过 props 向组件传递一个图片的src。
<MousePosition imgsrc={‘/logo2.gif’}/>
再次增加需求
不在使用图片,而是为鼠标添加注释, 或者还有鼠标滑动效果,或是一些组件等...
这时候就不得不去重新打量这个组件,再次复制一份?这样太抵效了,代码冗余较多。
思考-解决
是否有个即可以传递状态,也可传递组件的属性。所以就该使用 Render Props 来解决问题了。
首先需要将组件划分出来。分为鼠标移动组件与其他需要用到鼠标移动组件中的状态的组件。
鼠标移动组件:
class MousePosition extends Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
const {x,y} = this.state;
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)} //使用 this.props.render()将状态渲染出去。
</div>
);
}
}
其他组件-图片为例:
export default class Heart extends Component {
render() {
const mouse = this.props.mouse; //设 mouse 为MousePositon渲染出来的 state
return (
<div>
<img style={{width:50,height:50,
position:'absolute',
left : mouse.x,top : mouse.y}}
src="/icon.gif" alt="icon" />
</div>
)
}
}
使用
export default class App extends Component {
render(){
return (
<div>
<MousePosition render={ mouse =>(<Heart mouse={mouse}></Heart>)}/>
</div>)
}
}
使用 render 属性将渲染出来的 state 命名为 mouse,并以props 的形式传递给 Heart组件。
这样既保证的组件的可复用性。也解决了分享 state 的问题。
较为官方的话语:具有 render prop 的组件接受一个函数,该函数返回一个 React 元素并调用它而不是实现自己的渲染逻辑。
通俗讲就是可以使用自身的一些状态去渲染一个非自身的元素。