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>
    );
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值