反应forwardRef HoC没有引用容器元素

时间:2018-12-19 10:36:14

标签: javascript reactjs higher-order-components

我正在尝试在其空间外部单击时为闭合元素构建通用HOC(外部解决方案上的通用close)。

如我所见,这可以通过forwardRef和HOC实现来实现,尽管官方文档中有一个示例,但我似乎无法正确理解。

因此,我希望我的HOC创建对组件容器的引用。它具有包装功能,因为它具有处理程序来跟踪点击并对其执行操作。例如,假设我们有一个通用的Dropdown组件,人们希望我可以在该组件区域之外的任何单击上将其关闭。

我当前拥有的代码:

import React from 'react';

function withClose(Component) {
 class ClickContainer extends React.Component {
    constructor() {
      super();
      this.handleClose = this.handleClose.bind(this);
    }

    componentDidMount() {
      document.addEventListener('click', this.handleClose);
    }

    componentWillUnmount() {
      document.removeEventListener('click', this.handleClose);
    }

    handleClose(e) {
      // I expect having here context of container of wrapped component to do something like
      const { forwardedRef } = this.props; // <- I expect having context in forwardedRef variable
    }

    render() {
      const { forwardedRef, ...rest } = this.props;
      return <Component ref={forwardedRef} {...rest} />;
    }
  }

  return React.forwardRef((props, ref) => {
    return <ClickContainer {...props} forwardedRef={ref} />;
  });
}

export default withClose;

我在这里想念什么?我无法使其工作,我只能获得包装组件的上下文,而不是元素本身。

谢谢你!

2 个答案:

答案 0 :(得分:1)

几天前我实现了同样的事情。 我想要一个处理onClickoutside

的HOC

我希望该组件在每次点击时检查孩子是否被点击以及是否确实在孩子上调用了功能。

这是我的解决方案:

import React from 'react';

export default function withClickOutside(WrappedComponent) {
  class WithClickOutside extends WrappedComponent {
    constructor(props) {
      super(props);
      this.handleClickOutside = this.handleClickOutside.bind(this);
      this.wrappedElement = React.createRef();
    }

    componentDidMount() {
      document.addEventListener('click', this.handleClickOutside, true);
    }

    componentWillUnmount() {
      document.removeEventListener('click', this.handleClickOutside, true);
    }


    handleClickOutside(event) {
       if (event.type !== 'click') return;
       if (!this.wrappedElement.current) {
          throw new Error(`No ref for element ${WrappedComponent.name}. Please create ref when using withClickOutside`);
        }

      if (!this.wrappedElement.current.contains(event.target)) {
         if (!this.onClickOutside) {
           throw new Error(`${WrappedComponent.name} does not implement onClickOutside function. Please create onClickOutside function when using withClickOutside`);
         }

       this.onClickOutside(event);
     }
   }


   render() {
     const wrapped = super.render();
     const element = React.cloneElement(
       wrapped,
       { ref: this.wrappedElement },
     );

     return element;
   }
 }

 return WithClickOutside;
}

然后包装的组件必须实现一个名为onClickOutside的函数。

答案 1 :(得分:1)

Ref应该向下传递到元素

结帐https://codesandbox.io/s/7yzoqm747x

假设

export const Popup = (props,) => {
  const { name, forwardRef } = props;
  return (
   <div ref={forwardRef}>  // You need to pass it down from props
     {name}
   </div>
  )
}

HOC

export function withClose(Component) {
  class ClickContainer extends React.Component {
    constructor() {
     super();
     this.handleClose = this.handleClose.bind(this);
    }

    componentDidMount() {
      document.addEventListener('click', this.handleClose);
    }

    componentWillUnmount() {
      document.removeEventListener('click', this.handleClose);
    }

    handleClose(e) { 
     const { forwardRef } = this.props;
     console.log(forwardRef);
    }

    render() {
      const { forwardRef, ...rest } = this.props;
      return <Component forwardRef={forwardRef} {...rest} />;
    }
  }

  return React.forwardRef((props, ref) => {
    return <ClickContainer {...props} forwardRef={ref} />;
  });
}

并期待

 const CloseablePopup = withClose(Popup);

  class App extends Component {
    popupRef = React.createRef();
    render() {
      return (<CloseablePopup ref={popupRef} name="Closable Popup" />);
    }
  }