1

I'm using react useEffect hook to create a dropdown component, and added an event listener to close the drop down when the user click out side the drop down, but also have created a button to hide the whole dropdown, in that case the component wouldn't exist after clicking that button so automatically the ref attached to it will equal to null, so I had to clean up by removing the event listener when the drop down is hidden.

The dropdown work well but when I click on the button that hide it it give me an error that says

TypeError: Cannot read property 'contains' of null

I guess the problem is in the clean up function that remove the event listener not working when the user click the button to hide the dropdown entirely, the ref.current was supposed to be cleaned up when the component get hidden because it will be equal to null.

enter image description here

useEffect(() => {
  const onBodyClick = (event) => {
    if (ref.current.contains(event.target)) {
      return;
    }
    setopen(false);
  };
  document.body.addEventListener("click", onBodyClick);
  return () => {
    document.body.removeEventListener("click", onBodyClick);
  };
}, []);
Sofiane
  • 95
  • 1
  • 10
  • `ref.current` is `null` – blex Dec 13 '20 at 19:56
  • 1
    [Why not upload images of code/errors when asking a question?](https://meta.stackoverflow.com/questions/285551/why-not-upload-images-of-code-errors-when-asking-a-question) – charlietfl Dec 13 '20 at 19:57
  • @blex yes i know, it get null after i click on the button that hide the component that has the ref, the cleanup function supposed to clean the function that invoke when it get hidden but it is not working – Sofiane Dec 13 '20 at 20:11
  • @charlietfl i thought i did, can't you see it? i'm new to this site – Sofiane Dec 13 '20 at 20:11
  • While it looks like the onBodyClick function is the same when you create and remove the event listener, I would check that its not being created twice (or more) and removed once (leaving behind another listener) – NiRR Dec 13 '20 at 21:41
  • @Sofiane, that should work. Can you please post the code for the button you created to hide the Dropdown? – Daniel Dec 24 '20 at 02:50

3 Answers3

0

:v you invoke useRef without passing an initial value and then use useEffect so that is why it is null: https://reactjs.org/docs/hooks-reference.html#useref

  • the drop down worked so well, it just give me that error when i click the button that hide it entirely, when it get hidden ref.current automatically will equal to null because the component attached to it wouldn't exist anymore, that's why i added the clean up function to remove the event listener when the component get hidden but I guess it is not removing it right. – Sofiane Dec 13 '20 at 21:17
  • oh. i think it has something to do with the dependency array: https://stackoverflow.com/questions/58579426/in-useeffect-whats-the-difference-between-providing-no-dependency-array-and-an –  Dec 13 '20 at 21:25
  • @Sofiane, I do not think there is enough to go on here until you paste the code for the button you created that hides the dropdown component. I say that because what you have should work, so please paste that code. – Daniel Dec 24 '20 at 02:52
  • I've already edited the code, now i added navigation instead of that button that hide the component, and still getting the same problem, like when i navigate to that component than navigate to an other one the error occur, because when i go an other component the one with ref don't render any more so it give that error because it doesn't get cleaned up, I'm guessing maybe there is a react update and I'm learning from an old source because that same code worked for the teacher, do you still wanna see it? – Sofiane Dec 30 '20 at 21:15
0

I had the same error appear in the console. After some digging I found a few sources that mention a bug in react 17 - https://legacy.reactjs.org/blog/2020/08/10/react-v17-rc.html#potential-issues

Apparently that can also affect the libraries that are installed in the project. https://github.com/facebook/react/issues/20080

The solution that worked for me was to capture the ref in the useEffect:

  useEffect(() => {
    const refCaptured = ref.current;
    refCaptured.addEventListener("click", onBodyClick);
    return () => {
      refCaptured.removeEventListener("click", onBodyClick);
    };
  }, []);
VieraB
  • 43
  • 3
0

The ref.current value becomes null after the dropdown is hidden, and you're trying to access the conains method on ref.current. To resolve this issue, you can check if ref.current is not null before accesing its propetries..

useEffect(() => {
  const onBodyClick = (event) => {
    if (ref.current && ref.current.contains(event.target)) {
      return;
    }
    setopen(false);
  };

  document.body.addEventListener("click", onBodyClick);

  return () => {
    document.body.removeEventListener("click", onBodyClick);
  };
}, []);
traizooo
  • 144
  • 14