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?
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",
},
});