Yapei Li

专注于前端领域

0%

React 渲染属性(Render Props)

渲染属性(Render Props)

用于使用一个值为函数prop React 组件之间代码共享(数据共享)

带有渲染属性(Render Props)的组件需要一个返回 React 元素并调用它的函数,而不是实现自己的渲染逻辑

1
2
3
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>

组件是React 主要的代码复用单元,但如何共享状态一个组件的行为封装到其他需要相同状态的组件中并不是很明了

下面的组件在 web 应用追踪鼠标位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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}>
<h1>Move the mouse around!</h1>
<p>The current mouse position is ({this.state.x}, {this.state.y})</p>
</div>
);
}
}

在不使用render props时别的组件想使用鼠标坐标时只能包含Mouse组件或者用Mouse组件包含:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//包含Mouse
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse />
</div>
);
}
}

//用Mouse包含
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}>
//你要使用鼠标坐标的组件
<Cat mouse={this.state} />
</div>
);
}
}

使用 render prop:

提供一个带有函数 prop<Mouse> 组件,它能够动态决定什么需要渲染的,而不是将 <Cat> 硬编码到 <Mouse> 组件里

1
2
3
4
5
6
7
8
9
class Cat extends React.Component {
render() {
//3、通过 props获取父级组件(带render属性的组件)中的传递过来的mouse数据
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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}>

{/*
2、 使用`render` prop来动态确定要渲染的内容(分发 render)
并将state传入其中
调用props中的函数render,并将state传入其中
this.props.render(this.state)执行的结果就是返回的Cat组件
*/}
{this.props.render(this.state)}

</div>
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//父组件
class MouseTracker extends React.Component {
render() {
//1、将Cat组件添加到 Mouse组件的render属性(函数)中
//确定要传入的mouse数据
//render是一个函数类型的props
return (
<div>
<h1>Move the mouse around!</h1>
{//1、将Cat组件添加到 Mouse组件的render属性中确定要传入的mouse数据}
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
</div>
);
}
}

虽然这一模式被称为 “render props” ,但是函数类型的 prop``不必非叫render,可以叫任何名称

1
2
3
<Mouse aaa={mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}/>

不是必须返回一个 JSX 元素。相反,你可以直接返回一个元素!

1
2
3
4
5
<Mouse>
{mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}
</Mouse>

兼容写法(避免了render prop抵消 React.PureComponent 带来的优势)

将render函数 定义为父组件内部的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.renderTheCat = this.renderTheCat.bind(this);
}

renderTheCat(mouse) {
return <Cat mouse={mouse} />;
}

render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse render={this.renderTheCat} />
</div>
);
}
}