React 的 Render Props

588 阅读4分钟

术语 “render prop” 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术

什么是Render Props

官网是这样介绍的:

The term “render prop” refers to a simple technique for sharing code between React components using a prop whose value is a function.

大概意思是:给组件添加一个props值是函数,这个函数可以在组件渲染(render)的时候调用,那这个组件是干啥用的呢?就是为了给原有组件“注入”其它组件的代码。

在什么情况下使用Render Props

a render prop is a function prop that a component uses to know what to render.

翻译过来就是说,这个render prop就是让组件知道自己渲染什么东西。

如果你一个组件不知道自己渲染什么东西,或者说你一个组件的基础功能是提供”可变数据源“,具体展示UI可以从外部注入,那么就可以用这个技术了。

更具体地说,render prop 是一个用于告知组件需要渲染什么内容的函数 prop。

使用 Render Props 来解决横切关注点(Cross-Cutting Concerns)

组件是 React 代码复用的主要单元,但如何分享一个组件封装到其他需要相同 state 组件的状态或行为并不总是很容易。

可变数据源组件

例如:现在 组件封装了所有关于监听 mousemove 事件和存储鼠标 (x, y) 位置的行为,在这里,我们说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}>

        {/*
          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>
    );
  }
}

假设我们有一个 组件,它可以呈现一张在屏幕上追逐鼠标的猫的图片,之后可能还有类似渲染dog,pig需求。 这也是 render prop 的来历:我们提供一个带有函数 prop 的 组件,它能够动态决定什么需要渲染的。

被注入UI组件

这个Cat组件就是我们希望渲染在页面的UI了。

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 }} />
    );
  }
}

Mouse组件Render Props渲染Cat

class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>移动鼠标!</h1>
        <Mouse render={mouse => (
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}

首先我们知道组件的鼠标的位置,但其他组件cat, dog, pig也需要知道鼠标位置来根据坐标显示不同的图片,这时就需要将的state共享到其他组件,就可以使用Render Props。

这项技术使我们共享行为非常容易。要获得这个行为,只要渲染一个带有 render prop 的 组件就能够告诉它当前鼠标坐标 (x, y) 要渲染什么。

关于 render prop 一个有趣的事情是你可以使用带有 render prop 的常规组件来实现大多数高阶组件 (HOC)。 例如,如果你更喜欢使用 withMouse HOC而不是 组件,你可以使用带有 render prop 的常规 轻松创建一个:

将 Render Props 与 React.PureComponent 一起使用时要小心

如果使用render Props技术的组件(也就是有一个render函数属性的组件)是继承自React.PureComponent的,例如如果 Mouse 继承自 React.PureComponent,那么在props的浅比较看来,新老props总是不一样的,那么就会每次都重新渲染,性能开销比较大。

在这样例子中,每次 <MouseTracker> 渲染,它会生成一个新的函数作为 <Mouse render> 的 prop,因而在同时也抵消了继承自 React.PureComponent 的 组件的效果!

为了绕过这一问题,有时你可以定义一个 prop 作为实例方法,类似这样:

class MouseTracker extends React.Component {
  // 定义为实例方法,`this.renderTheCat`始终
  // 当我们在渲染中使用它时,它指的是相同的函数
  renderTheCat(mouse) {
    return <Cat mouse={mouse} />;
  }

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

参考文章

官网 Render Props
React 重温之Render Props
React 中的 Render Props React组件设计实践总结04 - 组件的思维