Answer a question

I have a React HOC that hides a flyover/popup/dropdown, whenever I click outside the referenced component. It works perfectly fine when using local state. My HOC looks like this:

export default function withClickOutside(WrappedComponent) {
  const Component = props => {
    const [open, setOpen] = useState(false);
    const ref = useRef();

    useEffect(() => {
      const handleClickOutside = event => {
        if (ref?.current && !ref.current.contains(event.target)) {
          setOpen(false);
        }
      };
      document.addEventListener('mousedown', handleClickOutside);
      return () => setOpen(false);
    }, [ref]);

    return <WrappedComponent open={open} setOpen={setOpen} ref={ref} {...props} />;
  };

  return Component;
} 

When in use I just wrap up the required component with the HOC function

const TestComponent = () => {
  const ref = useRef();
  return <Wrapper ref={ref} />;
}
export default withClickOutside(TestComponent);

But I have some flyover containers that are managed from Redux when they are shown, or when they are hidden. When the flyover is shown, I want to have the same behavior, by clicking outside the referenced component to hide it right away. Here's a example of a flyover:

  const { leftFlyoverOpen } = useSelector(({ toggles }) => toggles);

  return (
    <div>
      <Wrapper>
        <LeftFlyoverToggle
          onClick={() => dispatch({ type: 'LEFT_FLYOVER_OPEN' })}
        >
        ...
      </Wrapper>
      {leftFlyoverOpen && <LeftFlyover />}
      {rightFlyoverOpen && <RightFlyover />}
    </div>
  );

Flyover component looks pretty straightforward:

const LefFlyover = () => {
  return <div>...</div>;
};

export default LefFlyover;

Question: How can I modify the above HOC to handle Redux based flyovers/popup/dropdown?

Ideally I would like to handle both ways in one HOC, but it's fine if the examples will be only for Redux solution

Answers

You have a few options here. Personally, I don't like to use HOC's anymore. Especially in combination with functional components.

One possible solution would be to create a generic useOnClickOutside hook which accepts a callback. This enables you to dispatch an action by using the useDispatch hook inside the component.

export default function useOnClickOutside(callback) {
  const [element, setElement] = useState(null);

  useEffect(() => {
    const handleClickOutside = event => {
      if (element && !element.contains(event.target)) {
        callback();
      }
    };

    if (element) {
      document.addEventListener('mousedown', handleClickOutside);
    }

    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, [element, callback]);

  return setElement;
}
function LeftFlyOver() {
  const { leftFlyoverOpen } = useSelector(({ toggles }) => toggles);
  const dispatch   = useDispatch();
  const setElement = useOnClickOutside(() => {
    dispatch({ type: 'LEFT_FLYOVER_CLOSE' });
  });

  return (
    <Dialog open={leftFlyoverOpen} ref={ref => setElement(ref)}>
      ...
    </Dialog>
  )
}
Logo

React社区为您提供最前沿的新闻资讯和知识内容

更多推荐