React 学习笔记(7)—— Render Props

232 阅读1分钟

render prop 是一种通过函数类型的 prop 在 React 组件间共享逻辑的技术。具有 render prop 的组件接收一个返回值为 React 组件的函数,并在自身内部调用此函数来实现渲染逻辑。

基本范式

import React from "react";

class Mouse extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      x: 0,
      y: 0,
    };
    this.handleMouseMove = this.handleMouseMove.bind(this);
  }
  
  handleMouseMove(e) {
    this.setState({
      x: e.clientX,
      y: e.clientY,
    });
  }

  render() {
    return (
      <div style={{ height: "100vh" }} onMouseMove={this.handleMouseMove}>
        {this.props.children(this.state)}
      </div>
    );
  }
}

class Box extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    const mouse = this.props.mouse;
    return (
      <div
        style={{
          width: "10px",
          height: "10px",
          backgroundColor: "red",
          position: "absolute",
          left: mouse.x,
          top: mouse.y,
        }}
      />
    );
  }
}

export class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <p>请移动鼠标!</p>
        <Mouse>{(props) => <Box mouse={props} />}</Mouse>
      </div>
    );
  }
}

render prop 是一个告知组件需要渲染什么内容的函数 prop。使用 render prop 时需要约定函数入参。

使用 render prop 可以实现大多数高阶组件(HOC)。

function withMouse(WrappedComponent) {
  return class extends React.Component {
    render() {
      return (
        <Mouse>
          {(mouse) => <WrappedComponent {...this.props} mouse={mouse} />}
        </Mouse>
      );
    }
  };
}

render prop 由自身模式而得名,而属性名不必是 render。

注意事项

1. 和 React.PureComponent 一起使用

如果在 render 方法里面创建函数来使用 render prop,由于每次都生成新的函数,支持 render prop 的 React.PureComponent 组件将无法优化性能。

把传给 render prop 的函数保存为实例方法可以解决这一问题;如果该函数无法保存,则支持 render prop 的组件应继承自 React.Component

class Mouse extends React.PureComponent {
  // ...
}

class MouseTracker extends React.Component {
  renderBox(mouse) {
    return <Box mouse={mouse} />;
  }

  render() {
    return (
      <div>
        <p>请移动鼠标!</p>
        <Mouse>{this.renderBox}</Mouse>
      </div>
    );
  }
}