教你用 React Render Props

2,470 阅读2分钟

什么是 Render Props?

就是组件上名为 render 的 prop,该属性可以动态决定要渲染的内容

具体的写法如下:

<Component render={data => (
  <h1>Hello {data.target}</h1>
)}/>

也就是说,在 Component 组件内部渲染的时候,会调用 this.props.render 方法获取外部传过来的 jsx,来改变自己真正 render 时的效果。所以 render prop 是一个用于告知组件需要渲染什么内容的函数 prop。

为什么要用 Render Props?

目的就是为了组件的复用,把无关视图的逻辑抽象出来,例如下面的代码封装了鼠标追踪器的逻辑,能够根据鼠标的位置来实时更新坐标:

import React from 'react'

class MouseTracker extends React.Component {
  state = {
    x: 0,
    y: 0,
  }

  handleMouseMove = event => {
    this.setState({
      x: event.clientX,
      y: event.clientY,
    })
  }

  render() {
    return (
      <div onMouseMove={this.handleMouseMove} >
        {this.props.render(this.state)}
      </div>
    )
  }
}

export default MouseTracker

这个组件的作用就是输出鼠标坐标的值,并不关心当用户拿到了坐标值,如何做展示。所以把视图层的渲染内容交给了 render 属性处理:

import MouseTracker from './MouseTracker'

function Tracker() {
  return (
    <MouseTracker
      render={props => (
        <div style={{ height: '100vh' }}>
          {props.x},{props.y}
        </div>
      )}
    />
  )
}

export default Tracker

使用 Render Props 的注意事项

如果像上面写的一样,在 render 方法里面创建 render 函数的话,每次渲染都会创建一个新的函数,如果你的类继承自 PureComponent 的话,其实是没有起到作用的:

import MouseTracker from './MouseTracker'

class Tracker extends React.PureComponent {
  render() {
    return (
      <MouseTracker render={props => (
          <div style={{ height: '100vh' }}>
            {props.x},{props.y}
          </div>
        )}
      />
    )
  }
}

所以建议改成下面这个样子:

class Tracker extends React.PureComponent {
  renderView(props) {
    return (
      <div style={{ height: '100vh' }}>
        {props.x},{props.y}
      </div>
    )
  }
  render() {
    return <MouseTracker render={this.renderView} />
  }
}

和高阶函数 HOC 的对比

逻辑复用还有一种方式就是高阶函数(HOC),是可以达到相同的效果的,代码如下:

import React from 'react'

const withMouse = Component => {
  return class extends React.Component {
    state = { x: 0, y: 0 }

    handleMouseMove = event => {
      this.setState({
        x: event.clientX,
        y: event.clientY,
      })
    }

    render() {
      return (
        <div onMouseMove={this.handleMouseMove}>
          <Component {...this.props} {...this.state} />
        </div>
      )
    }
  }
}

export default withMouse

使用的时候也很简单,谁想获得高阶函数注入的 x 和 y 属性的话,就用 withMouse 包裹一下就好了:

import withMouse from './withMouse'

function Tracker(props) {
  return (
    <div style={{ height: '100vh' }}>
      {props.x},{props.y}
    </div>
  )
}

export default withMouse(Tracker)

但是这种写法总是没有 Render Props 那么直观,万一原来的组件也有 x 和 y 属性的话,就被覆盖了。当然 HOC 也有其使用场景,例如一个通用的高阶函数,用于判断组件的访问权限,有权限则显示,无权限则跳转。