将HOC重构为钩子

时间:2019-06-26 15:16:23

标签: javascript reactjs react-hooks high-order-component

在我花时间进行反应和写几篇文章的过程中,我在问自己如何将HOC重构为钩子,以及它是否有用以及为什么,

这是一个重构的小组件

function withSpacing(Component) {
  const WrappedWithSpacing = ({
    pv, ph, pt, pb, pl, pr, style, ...props
  }) => {

    const styleWithSpacing = {};

    const spacing = layout.padding;

    const paddingTop = pt || pv;
    const paddingBottom = pb || pv;
    const paddingRight = pr || ph;
    const paddingLeft = pl || ph;

    if(paddingTop > 0) styleWithSpacing.paddingTop = paddingTop * spacing;
    if(paddingBottom > 0) styleWithSpacing.paddingBottom = paddingBottom * spacing;
    if(paddingLeft > 0) styleWithSpacing.paddingLeft = paddingLeft * spacing;
    if(paddingRight > 0) styleWithSpacing.paddingRight = paddingRight * spacing;

    return <Component style={{...style, ...styleWithSpacing}} {...props} /> 
  }

  WrappedWithSpacing.propTypes = {
    pv: PropTypes.number,
    ph: PropTypes.number,
    pt: PropTypes.number,
    pb: PropTypes.number,
    pl: PropTypes.number,
    pr: PropTypes.number,
    style: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  }

  WrappedWithSpacing.defaultProps = {
    pv: 0,
    ph: 0,
    pt: 0,
    pb: 0,
    pr: 0,
    pl: 0,
  }

  return WrappedWithSpacing;
}

export default withSpacing;

根据官方文档:

  

挂钩可以代替渲染道具和更高阶的组件吗?

     

通常,渲染道具和高阶组件仅渲染一个   儿童。我们认为,挂钩是服务此用例的更简单方法。那里   仍然是两种模式的地方(例如,虚拟滚动条   组件可能具有renderItem属性或可视容器   组件可能具有其自己的DOM结构)。但是在大多数情况下,Hooks   足够了,可以帮助减少树中的嵌套。

我使用此HOC只是为了向组件添加一些预定义的空间。

将其重构为钩子确实更好,请您解释一下为什么?

如果是,将其重构为钩子的最佳方法是什么?

1 个答案:

答案 0 :(得分:1)

TDLR; 因为您的HOC没有任何状态或订阅,所以没有充分的理由使用钩子重构组件。

React Hooks引入了一些新功能来做出反应,以补充其基于类的对应功能。 useStatethis.statedocs)进行了补充,作为在渲染器之间存储状态的一种功能方式。 useEffectcomponentDidMountcomponenetDidUnmount方法(docs)的补充,它们提供了一种方法来执行功能反应组件的建立和拆卸。

如果您从文档中获得了这样的HOC:

// This function takes a component...
function withSubscription(WrappedComponent, selectData) {
  // ...and returns another component...
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        data: selectData(DataSource, props)
      };
    }

    componentDidMount() {
      // ... that takes care of the subscription...
      DataSource.addChangeListener(this.handleChange);
    }

    componentWillUnmount() {
      DataSource.removeChangeListener(this.handleChange);
    }

    handleChange() {
      this.setState({
        data: selectData(DataSource, this.props)
      });
    }

    render() {
      // ... and renders the wrapped component with the fresh data!
      // Notice that we pass through any additional props
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}

并将其转换为功能组件,您可能最终会得到如下结果:

function withSubscription(WrappedComponent, selectData) {
  return function WrappedComponent (props) {
    const [myData, setMyData] = useState(null);

       useEffect(() => {
         const handleMyDataChange = newData => {
           setMyData(newData);
         }

         DataSource.addChangeListener(handleMyDataChange);

         return function cleanup() {
           DataSource.removeChangeListener(handleMyDataChange);
         };
       });

      return <WrappedComponent data={data} {...this.props} />;
}

您的填充和间距在每个渲染上都会重新生成。由于问题中的代码在渲染之间没有任何持久性,因此尝试将其重构为实现React Hooks的组件没有多大意义。 React Hooks更适合将基于类的React组件转换为功能性React组件。