0

Short question: how do I get an action to take effect on page-load for the app router equivalent of statically generated pages from static paths in NextJS?

Longer version, my page generation is working fine:

// app/layout.js
import Providers from '@/app/Providers';

export default function RootLayout({ children }) {
    return (
        <html lang='en'>
            <body>
                <Providers>
                        {children}
                </Providers>
            </body>
        </html>
    )
}

// app/Providers.js
'use client';

import TrackProvider from '@/utils/trackProvider';

function Providers({children}) {
    return (
        <TrackProvider>
            {children}
        </TrackProvider>
    )
}

export default Providers;

// app/sample/page.js
import { getLayoutData } from '@/utils/getLayoutData';

async function getPage() {
    const allData = await getLayoutData();
    const { page } = allData;
  
    return { page };
}

export default async function Page() {
    const { page } = await getPage();

    return <div>{page}</div>
}

The issue I have is that TrackProvider provides context for React Tracking. With the old pages router, I would register a page view with a custom hook:

// pages/[slug].js
const Page = (props) => {
  usePageTracking(props.page.slug);

  return props.actionPage ? <ActionLayout {...props} /> : <Layout {...props} />;
}

where usePageTracking is a wrapper around useEffect and useTracking from React Tracking:

// utils/trackPage.js
'use client';

import { useTracking } from 'react-tracking';
import { useEffect } from 'react';

export function usePageTracking(pageSlug) {
    const { trackEvent } = useTracking();

    useEffect(() => {
        trackEvent({
            eventType: 'pageView',
            page: pageSlug
        });
    }, [pageSlug]);
}

But I can't use this same methodology in the new app router, because the page generation is async:

export default async function Page() {
    const { page } = await getPage();
    usePageTracking(page.slug); // <-- this won't work :(

    return <div>{page}</div>
}

What's the right way to accomplish this migration?

Danziger
  • 19,628
  • 4
  • 53
  • 83
philolegein
  • 1,099
  • 10
  • 28

1 Answers1

1

You probably need to wrap that functionality in a client component:

export const PageTracking = ({ slug }) => {
  usePageTracking(slug)

  return null;
} 

So your page will end up looking like this:

export default async function Page() {
  const { page } = await getPage();

  return (
    <>
      <PageTracking slug={ page.slug } />

      <div>{ page }</div>
    </>
  )
}

There's a similar example / recommendation in Next.js useRouter migration documentation:

import { Suspense } from 'react'
import { NavigationEvents } from './components/navigation-events'

export default function Layout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}

        <Suspense fallback={null}>
          <NavigationEvents />
        </Suspense>
      </body>
    </html>
  )
}
Danziger
  • 19,628
  • 4
  • 53
  • 83