Render Props 可以看作一种动态的 containment。Containment 是将一个内层组件作为外层组件的 children,而 render props 是将内层组件本身通过一个 render
函数展示在外层组件中:
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: '100vh' }} 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 (
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
);
}
}
惯常的用法是,提供一个 render
函数,它接受外层组件的 state
作为参数,返回一个内层组件的实例。即上面代码中的:
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
与 HOC 类似,也可以用 withXXX
模式来代码上面的 MouseTracker
:
function withMouse(Component) {
return class extends React.Component {
render() {
return (
<Mouse render={mouse => (
<Component {...this.props} mouse={mouse} />
)}/>
);
}
}
}
const MouseTracker = withMouse(Cat);
当然你可以不用 render
作为函数名,用 children
或者其他有意义的名字也可以,但它们都被称为 render props。在你的组件 API 中,应该指明你所使用的 render props 是必须的:
Mouse.propTypes = {
children: PropTypes.func.isRequired
};
同时注意使用 render props 可能会导致 React.PureComponent
的好处被抵消。参考 React 文档。