SUNYIMIN / react-review

react
2 stars 0 forks source link

react中HOC组件、render props、hooks的优缺点? #23

Open SUNYIMIN opened 4 years ago

SUNYIMIN commented 4 years ago

react中如果组件之间有一些相同的逻辑,我可以通过hoc,renderProps,hooks这三种方式将组件间的通用逻辑抽离出来,方便组件间逻辑的复用。 那如果这三种方式都能实现,那么它们之间有什么区别?

SUNYIMIN commented 4 years ago

现在有这么一个需求,组件A和组件B同时需要展示鼠标的光标在浏览器中的位置

常规的做法:

export default class A extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      x: 0,
      y: 0
    };
    this.handleMouseMove = this.handleMouseMove.bind(this);
  }

  componentDidMount() {
    document.addEventListener('mousemove', this.handleMouseMove, false);
  }

  componentWillUnmount() {
    document.removeEventListener('mousemove', this.handleMouseMove, false);
  }

  handleMouseMove(e) {
    this.setState({
      x: e.clientX,
      y: e.clientY
    });
  }

  render() {
    const {x, y} = this.state;
    <>
      <div>A: ({x}, {y})</div>
    </>
  }
}
export default class B extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      x: 0,
      y: 0
    };
    this.handleMouseMove = this.handleMouseMove.bind(this);
  }

  componentDidMount() {
    document.addEventListener('mousemove', this.handleMouseMove, false);
  }

  componentWillUnmount() {
    document.removeEventListener('mousemove', this.handleMouseMove, false);
  }

  handleMouseMove(e) {
    this.setState({
      x: e.clientX,
      y: e.clientY
    });
  }

  render() {
    const {x, y} = this.state;
    <>
      <div>B: ({x}, {y})</div>
    </>
  }
}

很明显可以看出组件A和组件B之前在获取光标位置的这段逻辑是重复的。我们可以尝试上面三种方式,将这段逻辑抽离出来

SUNYIMIN commented 4 years ago

hoc的方式:

export default function(title) {
  return function (WrappedComponent) {
    return class HOC extends Component {
      constructor(props) {
         super(props)
         this.state = {
          x: 0,
          y: 0
        };
        this.handleMouseMove = this.handleMouseMove.bind(this);
      }
      componentDidMount() {
        console.log(111)
        document.addEventListener('mousemove', this.handleMouseMove, false);
      }

      componentWillUnmount() {
        document.removeEventListener('mousemove', this.handleMouseMove, false);
      }

      handleMouseMove(e) {
        this.setState({
          x: e.clientX,
          y: e.clientY
        });
      }
      render() {
        const {x, y} = this.state;
        return (
          <div>
            <div>{title}</div>
           <WrappedComponent x={x} y={y}/>
          </div>
        )
      }
    }
  }
}
SUNYIMIN commented 4 years ago

renderProps的方式

import React from 'react'
export default class Provide extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      x: 0,
      y: 0
    };
    this.handleMouseMove = this.handleMouseMove.bind(this);
  }

  componentDidMount() {
    document.addEventListener('mousemove', this.handleMouseMove, false);
  }

  componentWillUnmount() {
    document.removeEventListener('mousemove', this.handleMouseMove, false);
  }

  handleMouseMove(e) {
    this.setState({
      x: e.clientX,
      y: e.clientY
    });
  }

  render() {
    const {x, y} = this.state;
    return this.props.render(x, y)
  }
}
SUNYIMIN commented 4 years ago

hooks的方式

const useXy = (props) => {
  //useState可以传入函数,延迟初始值的渲染
  const [position, setPosition] = useState(() => {
    return {
      x: 0,
      y: 0
    }
  })
  function handleMouseMove(e) {
    setPosition({
      x: e.clientX,
      y: e.clientY,
    });
  }
  useEffect((e) => {
    // 注册事件
    console.log(11)
    document.addEventListener('mousemove', handleMouseMove, false);
    // 销毁事件
    return () => {
      document.removeEventListener('mousemove', handleMouseMove, false);
    };
  }, [position]);
  //当position改变的时候useEffect才会执行,空数组的话useEffect只执行一次
  return position;
}

export default useXy
SUNYIMIN commented 4 years ago

总结三者的优缺点: