mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-04 10:30:00 -06:00
fix: prevent TypeError in useClickOutside hook during component unmounting
- Add DOM element validation before calling contains() method - Fix race condition where ref.current becomes non-DOM object during React unmounting - Apply fix to both apps/web and packages/surveys useClickOutside hooks - Resolves Sentry issue FORMBRICKS-CLOUD-3WH The issue occurred when React temporarily assigns non-DOM objects to refs during component unmounting, causing "t.contains is not a function" error. The fix adds proper validation to ensure contains() is only called on actual DOM elements.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -75,3 +75,4 @@ infra/terraform/.terraform/
|
||||
packages/ios/FormbricksSDK/FormbricksSDK.xcodeproj/project.xcworkspace/xcuserdata
|
||||
.cursorrules
|
||||
i18n.cache
|
||||
formbricks-quickstart/docker-compose.yml
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { RefObject, useEffect } from "react";
|
||||
|
||||
// Helper function to check if a value is a DOM element with contains method
|
||||
const isDOMElement = (element: any): element is HTMLElement => {
|
||||
return element && typeof element.contains === "function" && element.nodeType === Node.ELEMENT_NODE;
|
||||
};
|
||||
|
||||
// Improved version of https://usehooks.com/useOnClickOutside/
|
||||
export const useClickOutside = (
|
||||
ref: RefObject<HTMLElement | HTMLDivElement | null>,
|
||||
@@ -13,14 +18,14 @@ export const useClickOutside = (
|
||||
// Do nothing if `mousedown` or `touchstart` started inside ref element
|
||||
if (startedInside || !startedWhenMounted) return;
|
||||
// Do nothing if clicking ref's element or descendent elements
|
||||
if (!ref.current || ref.current.contains(event.target as Node)) return;
|
||||
if (!isDOMElement(ref.current) || ref.current.contains(event.target as Node)) return;
|
||||
|
||||
handler(event);
|
||||
};
|
||||
|
||||
const validateEventStart = (event: MouseEvent | TouchEvent) => {
|
||||
startedWhenMounted = ref.current !== null;
|
||||
startedInside = ref.current !== null && ref.current.contains(event.target as Node);
|
||||
startedWhenMounted = isDOMElement(ref.current);
|
||||
startedInside = isDOMElement(ref.current) && ref.current.contains(event.target as Node);
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", validateEventStart);
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { MutableRef, useEffect } from "preact/hooks";
|
||||
|
||||
// Helper function to check if a value is a DOM element with contains method
|
||||
const isDOMElement = (element: any): element is HTMLElement => {
|
||||
return element && typeof element.contains === "function" && element.nodeType === Node.ELEMENT_NODE;
|
||||
};
|
||||
|
||||
// Improved version of https://usehooks.com/useOnClickOutside/
|
||||
export const useClickOutside = (
|
||||
ref: MutableRef<HTMLElement | null>,
|
||||
@@ -13,14 +18,14 @@ export const useClickOutside = (
|
||||
// Do nothing if `mousedown` or `touchstart` started inside ref element
|
||||
if (startedInside || !startedWhenMounted) return;
|
||||
// Do nothing if clicking ref's element or descendent elements
|
||||
if (!ref.current || ref.current.contains(event.target as Node)) return;
|
||||
if (!isDOMElement(ref.current) || ref.current.contains(event.target as Node)) return;
|
||||
|
||||
handler(event);
|
||||
};
|
||||
|
||||
const validateEventStart = (event: MouseEvent | TouchEvent) => {
|
||||
startedWhenMounted = ref.current !== null;
|
||||
startedInside = ref.current !== null && ref.current.contains(event.target as Node);
|
||||
startedWhenMounted = isDOMElement(ref.current);
|
||||
startedInside = isDOMElement(ref.current) && ref.current.contains(event.target as Node);
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", validateEventStart);
|
||||
|
||||
Reference in New Issue
Block a user