clickOutside hook doesn't work on 2nd click

Viewed 52

I have a list of items and want to include an icon that opens a modal for a user to choose 'edit' or 'delete' the item.

And I put this code inside the ActionModal so that only clicked modal would open by comparing the ids. The problem is, clicking outside the element work only one time and after that, nothing happens when the ellipsis button clicked. I think it's probably because the state inside ActionModal, 'modalOpen' remains false, but I'm stuck here and don't know how to handle it.

if (!isOpen.show || isOpen.id !== id || !modalOpen) return null;

enter image description here

const List = () => {
  const [modal, setModal] = useState({ id: null, show: false });

  const onDialogClick = (e) => {
    setModal((prevState) => {
      return { id: e.target.id, show: !prevState.show };
    });
  };

  const journals = journals.map((journal) => (
    <StyledList key={journal.id}>
      <Option>
        <FontAwesomeIcon
          icon={faEllipsisV}
          id={journal.id}
          onClick={onDialogClick}
        />
        <ActionModal
          actions={['edit', 'delete']}
          id={journal.id}
          isOpen={modal}
        ></ActionModal>
      </Option>
const ActionModal = ({ id, actions, isOpen }) => {
  const content = actions.map((action) => <li key={action}>{action}</li>);

  const ref = useRef();
  const [modalOpen, setModalOpen] = useState(true);

  useOnClickOutside(ref, () => setModalOpen(!modalOpen));

  if (!isOpen.show || isOpen.id !== id || !modalOpen) return null;
  return (
    <StyledDiv>
      <ul ref={ref}>{content}</ul>
    </StyledDiv>
  );
};

function useOnClickOutside(ref, handler) {
  useEffect(() => {
    const listener = (event) => {
      if (!ref.current || ref.current.contains(event.target)) {
        return;
      }
      handler(event);
    };
    document.addEventListener('mousedown', listener);
    document.addEventListener('touchstart', listener);
    return () => {
      document.removeEventListener('mousedown', listener);
      document.removeEventListener('touchstart', listener);
    };
  }, [ref, handler]);
}
1 Answers

So fistly you do not need to include ref and handler as dependencies in useEffect hook, because evvent listeners are set on initial load, there is no need to set it on every value change.

I think I don't fully understand your situation. So you need to close the modal after it is opened by pressing outside it? or you want to be able to press that 3 dots icon when it's opened?

P.S.

I little bit condesed your code. Try this and let me know what is happening. :)

const ActionModal = ({ id, actions, isOpen, setOpen }) => {
  const ref = useRef();

  const content = actions.map((action) => <li key={action}>{action}</li>);

  useEffect(() => {
    const listener = (event) => {
      if (ref.current || !ref.current.contains(event.target)) {
        setOpen({...open, show: false});
      }
    };

    document.addEventListener('mousedown', listener);
    document.addEventListener('touchstart', listener);

    return () => {
      document.removeEventListener('mousedown', listener);
      document.removeEventListener('touchstart', listener);
    };
  }, []);

  return (
    <StyledDiv>
      <div ref={ref}>
        <ul>{content}</ul>
      </div>
    </StyledDiv>
  );
};
Related