【问题标题】:React forwardRef HoC not giving reference to container element反应 forwardRef HoC 不参考容器元素
【发布时间】:2019-05-19 20:03:10
【问题描述】:

我正在尝试为单击其空间之外的关闭元素构建一个通用 HOC(外部解决方案的通用关闭)。

在我看来,这可以通过 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;

我在这里缺少什么?我不能让它工作,我只得到包装组件的上下文而不是元素本身。

非常感谢!

【问题讨论】:

  • 什么是this.containerRef?您需要将this.props.forwardedRef 传递给元素
  • @Orar 是的,对不起,这只是我尝试过的东西,我把它改成了正确的代码......

标签: javascript reactjs higher-order-components


【解决方案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" />);
    }
  }

【讨论】:

  • 基本上这是正确的解决方案,我错过了在容器中创建 ref ......虽然这不能完全回答我的问题。您知道是否可以在不提供父级引用的情况下实现它?这需要我在多个地方定义 refs,而不是让 HOC 自己处理所有事情。谢谢
  • HOC 传递 ref 而不被包装组件处理可能会导致错误,如果包装组件是一个函数。如果被包装的组件是一个类,则 ref 只能访问该类的实例,而不能访问容器元素。阅读这里reactjs.org/docs/refs-and-the-dom.html#accessing-refs
【解决方案2】:

我几天前实施了同样的事情。 我想要一个处理 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的函数。

【讨论】:

  • 这是一种解决方案,它可以工作,但有点hackish。如果可能的话,我想在没有 cloneElement 的情况下实现它。你能想到任何其他的实施方案吗?
  • 我知道它的 abit hack,但由于某种原因,采用此解决方案并试图使其在没有克隆元素的情况下工作并引用包装的组件并没有奏效。好处是您不需要处理包装组件中的 refs,因此您可以保存一些忘记添加的代码和错误
猜你喜欢
  • 2018-12-24
  • 1970-01-01
  • 2021-07-13
  • 2022-10-09
  • 2017-03-26
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
  • 1970-01-01
相关资源
最近更新 更多