fix: add null checks for document and firebase messaging to prevent SSR errors

Added defensive programming by adding null checks for document object before accessing its properties and methods across multiple components (MultiSelect, ConfirmationModal, Dropdown, Modal). Also implemented lazy initialization for Firebase messaging to prevent SSR-related errors and ensure messaging is only initialized on the client side. These changes improve server-side rendering compatibility and prevent potential runtime errors.
This commit is contained in:
AmirReza Jamali
2025-11-15 12:22:30 +03:30
parent 5e60a292f5
commit 1d1027fab4
6 changed files with 50 additions and 17 deletions
+2 -1
View File
@@ -130,8 +130,9 @@ function MultiSelect<T extends FieldValues>({
return;
setShow(false);
};
document?.addEventListener("click", clickHandler);
return () => document.removeEventListener("click", clickHandler);
return () => document?.removeEventListener("click", clickHandler);
}, [show]);
return (
+9 -6
View File
@@ -8,12 +8,15 @@ import { toast } from "react-toastify";
const Notification = ({ children }: PropsWithChildren) => {
useEffect(() => {
onMessage(messaging, (payload) => {
console.log("Message received. ", payload);
if (payload.notification) {
toast.success(payload.notification.title ?? "New Notification");
}
});
const messagingInstance = messaging();
if (messagingInstance) {
onMessage(messagingInstance, (payload) => {
console.log("Message received. ", payload);
if (payload.notification) {
toast.success(payload.notification.title ?? "New Notification");
}
});
}
}, []);
return <>{children}</>;
+3 -3
View File
@@ -54,12 +54,12 @@ const ConfirmationModal: React.FC<ConfirmationModalProps> = (props) => {
if (isOpen) {
document?.addEventListener("keydown", handleEscape);
document.body.style.overflow = "hidden";
if (document) document.body.style.overflow = "hidden";
}
return () => {
document.removeEventListener("keydown", handleEscape);
document.body.style.overflow = "unset";
document?.removeEventListener("keydown", handleEscape);
if (document) document.body.style.overflow = "unset";
};
}, [isOpen, onCancel]);
+2 -2
View File
@@ -46,9 +46,9 @@ export function Dropdown({ children, isOpen, setIsOpen }: DropdownProps) {
if (isOpen) {
triggerRef.current = document.activeElement as HTMLElement;
document.body.style.pointerEvents = "none";
if (document) document.body.style.pointerEvents = "none";
} else {
document.body.style.removeProperty("pointer-events");
if (document) document.body.style.removeProperty("pointer-events");
setTimeout(() => {
triggerRef.current?.focus();
+3 -3
View File
@@ -33,13 +33,13 @@ const Modal: React.FC<ModalProps> = ({
useEffect(() => {
if (isOpen) {
document.body.style.overflow = "hidden";
if (document) document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "unset";
if (document) document.body.style.overflow = "unset";
}
return () => {
document.body.style.overflow = "unset";
if (document) document.body.style.overflow = "unset";
};
}, [isOpen]);
+31 -2
View File
@@ -2,6 +2,7 @@ import { COOKIE_KEYS } from "@/lib/shared/cookies";
import { initializeApp } from "firebase/app";
import { getMessaging, getToken } from "firebase/messaging";
import Cookies from "js-cookie";
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
@@ -11,11 +12,35 @@ const firebaseConfig = {
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};
// Initialize Firebase app
const app = initializeApp(firebaseConfig);
export const messaging = getMessaging(app);
// Lazy initialization of messaging to avoid SSR issues
let messaging: any = null;
const getMessagingInstance = () => {
if (typeof window !== 'undefined' && !messaging) {
try {
messaging = getMessaging(app);
} catch (error) {
console.warn('Failed to initialize Firebase Messaging:', error);
}
}
return messaging;
};
export const requestForToken = () => {
return getToken(messaging)
// Only run on client side
if (typeof window === 'undefined') {
return Promise.resolve(null);
}
const messagingInstance = getMessagingInstance();
if (!messagingInstance) {
return Promise.resolve(null);
}
return getToken(messagingInstance)
.then((currentToken) => {
if (currentToken) {
console.log("current token for client: ", currentToken);
@@ -27,9 +52,13 @@ export const requestForToken = () => {
);
// ...
}
return currentToken;
})
.catch((err) => {
console.log("An error occurred while retrieving token. ", err);
// ...
return null;
});
};
export { getMessagingInstance as messaging };