I'm trying to integrate CleverTap
into my Next.js
app. Followed the documentation
_app.tsx
import React, { useEffect, useState } from "react";
import type { AppProps } from "next/app";
import { appWithTranslation } from "next-i18next";
import { Hydrate, QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import nextI18NextConfig from "../next-i18next.config.js";
import "tailwindcss/tailwind.css";
import "styles/globals.scss";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import { useRouter } from "next/router";
import SvgPageLoading from "components/color-icons/PageLoading";
// import { PageLoading } from "components/color-icons/";
import { DefaultSeo } from 'next-seo';
import SEO from 'next-seo.config';
import {cleverTap} from "utils/cleverTapHelper";
cleverTap.initialize('TEST-61c-a12');
function MyApp({ Component, pageProps }: AppProps) {
const [queryClient] = React.useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
staleTime: Infinity,
},
},
})
);
const router = useRouter();
const [isAnimating, setIsAnimating] = useState(false);
useEffect(() => {
const handleStart = () => {
setIsAnimating(true);
};
const handleStop = () => {
setIsAnimating(false);
};
router.events.on("routeChangeStart", handleStart);
router.events.on("routeChangeComplete", handleStop);
router.events.on("routeChangeError", handleStop);
return () => {
router.events.off("routeChangeStart", handleStart);
router.events.off("routeChangeComplete", handleStop);
router.events.off("routeChangeError", handleStop);
};
}, [router]);
return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<DefaultSeo {...SEO} />
<Component {...pageProps} />
{isAnimating && (
<div className="fixed top-0 left-0 flex items-center justify-center w-screen h-screen overflow-visible bg-white bg-opacity-80 z-overlay top-z-index">
<SvgPageLoading />
</div>
)}
<ReactQueryDevtools initialIsOpen={false} />
</Hydrate>
</QueryClientProvider>
);
}
export default appWithTranslation(MyApp, nextI18NextConfig);
cleverTapHelper.ts
export const cleverTap = {
initialize: function (accountId) {
console.log('I see initialize req')
window.clevertap = {event: [], profile: [], account: [], onUserLogin: [], notifications: []};
window.clevertap.account.push({'id': accountId});
(function () {
var wzrk = document.createElement('script');
wzrk.type = 'text/javascript';
wzrk.async = true;
wzrk.src = ('https:' == document.location.protocol ? 'https://d2r1yp2w7bby2u.cloudfront.net' : 'http://static.clevertap.com') '/js/a.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wzrk, s);
})();
},
event: function (name, payload = {}) {
console.log('I see event req')
if (payload) {
window.clevertap.event.push(name, payload);
} else {
window.clevertap.event.push(name);
}
},
profile: function (payload) {
console.log('I see profile req')
window.clevertap.profile.push(payload);
},
logout: function () {
console.log('I see logout req')
window.clevertap.logout();
}
};
cleverTap.d.ts
declare global {
interface Window {
clevertap: any;
}
}
export {};
Window object should not be undefined
but getting undefined! What's going on?
CodePudding user response:
This is because NextJS is trying to execute that function on the server because it uses SSR, and window
is a browser object. Since the window
object is available only in the browser (client-side), the server is unable to identify the window
object, hence getting undefined
. In order to fix this, you should make sure that any functions/components that contain client-side related code be executed only on the browser or client-side. One way is using hooks such as useEffect
that run only after the component is mounted. Another way is to use lazy loading which pretty much does the same thing.
- Using
useEffect
hook. In your_app.tsx
component, add a newuseEffect
hook and move the initialization code into the newly createduseEffect
function.
useEffect(()=>{
cleverTap.initialize('TEST-61c-a12');
},[])
- Using lazy loading. (Dynamic import)
Instead of directly importing the function, import it dynamically and set server-side rendering to
false
:
import dynamic from 'next/dynamic'
const cleverTap = dynamic(()=>{
return import("utils/cleverTapHelper")
},
{ssr:false}
)
cleverTap.initialize('TEST-61c-a12');