React 之 Render Props

有时候,某些组建的各种功能及其处理逻辑几乎完全相同,只是显示的界面不一样,建议下面的方式选择其一来解决重复代码的问题(横切关注点)

props1

实现这两个组建,我们通常的写法是:

// MoveBox
class MoveBox extends PureComponent {
  state = {
    x: 0,
    y: 0
  };

  myRef = React.createRef();

  handleMove = e => {
    // e不是真实的dom对象,是经过包装的,不存在offsetX 或 offsetY
    //  但是其存在 pageX (距离页面 pageY) or clientX(距离视口 clientY)

    const { clientX, clientY } = e;
    const { left, top } = this.myRef.current.getBoundingClientRect();
    console.log(clientX - left);
    this.setState({
      x: clientX - left,
      y: clientY - top
    });
  };

  render() {
    return (
      <div className="moveBox" onMouseMove={this.handleMove} ref={this.myRef}>
        <div
          className="moveDiv"
          style={{
            left: `${this.state.x}px`,
            top: `${this.state.y}px`
          }}
        ></div>
      </div>
    );
  }
}

// MoveMessage
class MoveMessage extends PureComponent {
  state = {
    x: 0,
    y: 0
  };

  myRef = React.createRef();

  handleMove = e => {
    // e不是真实的dom对象,是经过包装的,不存在offsetX 或 offsetY
    //  但是其存在 pageX (距离页面 pageY) or clientX(距离视口 clientY)

    const { clientX, clientY } = e;
    const { left, top } = this.myRef.current.getBoundingClientRect();

    this.setState({
      x: clientX - left,
      y: clientY - top
    });
  };

  render() {
    return (
      <div className="moveBox" onMouseMove={this.handleMove} ref={this.myRef}>
        <p>
          x:{this.state.x},y:{this.state.y}
        </p>
      </div>
    );
  }
}

// MoveContainer
class MoveContainer extends PureComponent {
  render() {
    return (
      <div>
        <MoveBox />
        <MoveMessage />
      </div>
    );
  }
}

会发现存在很多重复的代码

1.模仿context.Consumer

​ 1.某个组件,需要某个属性

​ 2.该属性是一个函数,函数的返回值用于渲染

​ 3.函数的参数会传递为需要的数据

​ 4.注意纯组件的属性(尽量避免每次传递的render props的地址不一致)

// MoveContainer
class MoveContainer extends PureComponent {
  moveBox = value => (
    <div
      className="moveDiv"
      style={{
        left: `${value.x}px`,
        top: `${value.y}px`
      }}
    ></div>
  );

  moveMessage = value => (
    <p>
      x:{value.x},y:{value.y}
    </p>
  );
  render() {
    return (
      <div>
        <MoveIndex>{this.moveBox}</MoveIndex>
        <MoveIndex>{this.moveMessage}</MoveIndex>
      </div>
    );
  }
}

// MoveIndex  将MoveBox 和MoveMessage 合成一个组件,渲染内容用函数替代
class MoveIndex extends PureComponent {
  state = {
    x: 0,
    y: 0
  };

  myRef = React.createRef();

  handleMove = e => {
    // e不是真实的dom对象,是经过包装的,不存在offsetX 或 offsetY
    //  但是其存在 pageX (距离页面 pageY) or clientX(距离视口 clientY)

    const { clientX, clientY } = e;
    const { left, top } = this.myRef.current.getBoundingClientRect();

    this.setState({
      x: clientX - left,
      y: clientY - top
    });
  };

  render() {
    return (
      <div className="moveBox" onMouseMove={this.handleMove} ref={this.myRef}>
        {this.props.children(this.state)}
      </div>
    );
  }
}

2.render Props

​ 1.某个组件,需要某个属性

​ 2.该属性是一个函数,函数的返回值用于渲染

​ 3.函数的参数会传递为需要的数据

​ 4.注意纯组件的属性(尽量避免每次传递的render props的地址不一致)

​ 5.通常该属性的名字加做render

写法与1相似,

// MoveContainer
class MoveContainer extends PureComponent {
  moveBox = value => (
    <div
      className="moveDiv"
      style={{
        left: `${value.x}px`,
        top: `${value.y}px`
      }}
    ></div>
  );

  moveMessage = value => (
    <p>
      x:{value.x},y:{value.y}
    </p>
  );
  render() {
    return (
      <div>
           {/* 这个传入组件函数使用render这个属性 */}
        <MoveIndex render={this.moveBox} /> 
        <MoveIndex render={this.moveMessage} />
      </div>
    );
  }
}

// MoveIndex
class MoveIndex extends PureComponent {
  state = {
    x: 0,
    y: 0
  };

  myRef = React.createRef();

  handleMove = e => {
    // e不是真实的dom对象,是经过包装的,不存在offsetX 或 offsetY
    //  但是其存在 pageX (距离页面 pageY) or clientX(距离视口 clientY)

    const { clientX, clientY } = e;
    const { left, top } = this.myRef.current.getBoundingClientRect();

    this.setState({
      x: clientX - left,
      y: clientY - top
    });
  };

  render() {
    return (
      <div className="moveBox" onMouseMove={this.handleMove} ref={this.myRef}>
        {/* 组件内部 使用this.props.render接收 */}
        {this.props.render(this.state)}
      </div>
    );
  }
}

3.HOC

// MoveHoc
function MoveHoc(Comp) {
  return class MoveIndex extends PureComponent {
    state = {
      x: 0,
      y: 0
    };

    myRef = React.createRef();

    handleMove = e => {
      // e不是真实的dom对象,是经过包装的,不存在offsetX 或 offsetY
      //  但是其存在 pageX (距离页面 pageY) or clientX(距离视口 clientY)

      const { clientX, clientY } = e;
      const { left, top } = this.myRef.current.getBoundingClientRect();

      this.setState({
        x: clientX - left,
        y: clientY - top
      });
    };

    render() {
      return (
        <div className="moveBox" onMouseMove={this.handleMove} ref={this.myRef}>
          <Comp {...this.state} />
        </div>
      );
    }
  };
}

// MoveContainer
function MoveBox(props) {
  return (
    <div
      className="moveDiv"
      style={{
        left: `${props.x}px`,
        top: `${props.y}px`
      }}
    ></div>
  );
}

function MoveMessage(props) {
  return (
    <p>
      x:{props.x},y:{props.y}
    </p>
  );
}

const HocMoveBox = MoveHoc(MoveBox);
const HocMoveMessage = MoveHoc(MoveMessage);

export default class MoveContainer extends PureComponent {
  render() {
    return (
      <div>
        <HocMoveBox />
        <HocMoveMessage />
      </div>
    );
  }
}
发布了45 篇原创文章 · 获赞 14 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/cmchenmei/article/details/104023461