Detect click outside of component with React Native Web?

Viewed 1638

Im using React Native Web. I need to detect when a user clicks outside of a component. This is only loaded on web so it doesn't have to work on native.

Ive been trying to use a version of this hook: https://usehooks.com/useOnClickOutside/

In my component:

useOnClickOutside(ref, () => setIsOpen(false));

In the hook:

function useOnClickOutside(ref, handler) {
  React.useEffect(
    () => {
      const listener = event => {
        // Do nothing if clicking ref's element or descendent elements
        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],
  );
}

Which gives me an error:

TypeError: ref.current.contains is not a function

If I log ref.current I can see that there is no contains method. Does this exist for React Native Web?

1 Answers

NOTE: this is only for react-native-web

here is demo: https://snack.expo.io/@nomi9995/5b60ae

<View> is custom class-based React component so it returns React component instance in ref. And it does not have any DOM-specific methods like .contains

you can give unique className to View and access dom element by document.getElementsByClassName()[0]

import React, { useState, useEffect, useRef } from "react";
import { StyleSheet, Text, View, Button } from "react-native";

function useOnClickOutside(ref, handler) {
  useEffect(() => {
    const listener = (event) => {
      if (!ref.current || document.getElementsByClassName("uniqueClassName")[0].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]);
}

export default function App() {
  const ref = useRef();

  const [isModalOpen, setModalOpen] = useState(false);

  useOnClickOutside(ref, () => setModalOpen(false));

  return (
    <View style={styles.container}>
      {isModalOpen ? (
        <View ref={ref} style={styles.modalBox} className="uniqueClassName">
          <Text style={{ color: "#FFFFFF" }}>this is modalBox</Text>
        </View>
      ) : (
        <Button onPress={() => setModalOpen(true)} title="Open Modal" />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "red",
    alignItems: "center",
    justifyContent: "center",
  },
  modalBox: {
    backgroundColor: "blue",
    height: 200,
    width: 200,
    justifyContent: "center",
    alignItems: "center",
  },
});

enter image description here

Related