Home > database >  getServerSideProps and NextJS Typescript Layout
getServerSideProps and NextJS Typescript Layout

Time:06-03

Guys I am struggling with getting NextJS Layout working with Typescript. I know I am close. All of below works as expected!! but I cannot get the annoying squiggle to disappear when I pass props from getServerSideProps.

props {someText) is always underlined with squiggle - see link to screen shot

I realise I cannot pass these props to the Layout itself but my understanding is I should be able to pass to the page itself... and as I say the code does work!

Spent too long trying to resolve .. hoping someone on this forum can put me right:)

I am working around for now with {someText}:any

screenshot

This is my next.d.ts

import type { NextPage } from 'next';
import NextComponentType from 'next/dist/shared/lib/utils'
import type { AppProps } from 'next/app';

declare module 'next' {
    type Page<P = {}, IP = P> = NextPage<P, IP> & {
        getLayout?: (Component: NextComponentType) => JSX.Element;
    }
}

declare module 'next/app' {
    type AppPropsWithLayout<P = {}> = AppProps<P> & {
        //emotionCache?: EmotionCache
        Component: Page;
    }
}

I have below which is exported from _App.tsx (I have left some of the Recoil and MUI stuff in there so that you get the full picture).

// mui-app.tsx
import { RecoilRoot } from 'recoil'

import { useEffect, useState } from 'react';
import Head from 'next/head';
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import { CacheProvider, EmotionCache } from '@emotion/react';
import theme from './theme';
import createEmotionCache from './createEmotionCache';

import { SessionProvider } from 'next-auth/react';

import type { ReactNode} from 'react'
import { AppPropsWithLayout } from 'next/app';


interface MuiAppProps extends AppPropsWithLayout{
  emotionCache?: EmotionCache
}

// Client-side cache, shared for the whole session of the user in the browser.
const clientSideEmotionCache = createEmotionCache();


export function MuiApp(props: MuiAppProps):JSX.Element {
  const { Component, emotionCache = clientSideEmotionCache, pageProps: { session, ...pageProps } } = props;
  const getLayout = Component.getLayout ?? ((page: ReactNode): ReactNode => page)
  const [mounted, setMounted] = useState(false)
  useEffect(() => {
    setMounted(true)
  }, [])
  return (
    <SessionProvider session={pageProps.session} refetchInterval={0}>
      <RecoilRoot>

        <CacheProvider value={emotionCache}>
          <Head>
            <meta name="viewport" content="initial-scale=1, width=device-width" />
          </Head>
          <ThemeProvider theme={theme}>
            {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
            <CssBaseline />
            <div style={{ visibility: mounted ? 'visible' : 'hidden' }}>
              {getLayout(<Component {...pageProps} />)}
            </div>
          </ThemeProvider>
        </CacheProvider>

      </RecoilRoot>
    </SessionProvider>
  )
}

This is my index.tsx

import React, { ReactNode } from "react";
import type { Page } from 'next'
import { PageLayout } from "@app/UI/MUI/components/layout/page-layout";

const page: Page<{ someText: string }> = ({ someText }) => {
    return (
        <div>{someText}</div>
    )
}
export default page
page.getLayout = (page: Page) => {
    return (
        <PageLayout>{page}</PageLayout>
    )
}


export const getServerSideProps = async () => {

    return ({ props: { someText: 'This is a test' } })

}

CodePudding user response:

Try to change the type of the page to NextPage.
Also, a component name should be uppercased and you may want to type the getServerSideProps function as well:

import React, { ReactNode } from 'react';
import type { NextPage } from 'next';
import { PageLayout } from '@app/UI/MUI/components/layout/page-layout';

const Page: NextPage<{ someText: string }> = ({ someText }) => {
  return <div>{someText}</div>;
};

export default Page;

Page.getLayout = (page: NextPage) => {
  return <PageLayout>{page}</PageLayout>;
};

import { GetServerSideProps } from 'next';

export const getServerSideProps: GetServerSideProps = async () => {
  return { props: { someText: 'This is a test' } };
};

CodePudding user response:

Not certain exactly why my existing approach did not work but I have gone with the following...

modified and gave up on next.d.ts

   import { ReactNode, ReactElement } from 'react'
    import { NextPage } from 'next'
    import { AppProps } from 'next/app'
    import { CacheProvider, EmotionCache } from '@emotion/react';
    export type Page<P = {}, IP = P> = NextPage<P, IP> & {
        getLayout?: (page: ReactNode) => ReactNode;
    }
    
    export type AppPropsWithLayout<P> = AppProps<P> & {
        //emotionCache?: EmotionCache
        Component: Page<P>;

}

my new App.tsx

// pages/_app.tsx
//TODO: move impoerts and Recoil Provider to _App.ts
import { RecoilRoot } from 'recoil'

import { useEffect, useState } from 'react';
import Head from 'next/head';
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import { CacheProvider, EmotionCache } from '@emotion/react';
import theme from './theme';
import createEmotionCache from './createEmotionCache';

import { SessionProvider } from 'next-auth/react';

import type { ReactNode } from 'react'

import { AppPropsWithLayout } from '@app/types/app-types'//TODO: shorten


type MuiAppProps<P = {}> = AppPropsWithLayout<P> & {
  emotionCache?: EmotionCache
}

// Client-side cache, shared for the whole session of the user in the browser.
const clientSideEmotionCache = createEmotionCache();


export function MuiApp(props: MuiAppProps): JSX.Element {
  const { Component, emotionCache = clientSideEmotionCache, pageProps: { session, ...pageProps } } = props;
  const getLayout = Component.getLayout ?? ((page: ReactNode): ReactNode => page)
  const [mounted, setMounted] = useState(false)
  useEffect(() => {
    setMounted(true)
  }, [])
  return (
    <SessionProvider session={pageProps.session} refetchInterval={0}>
      <RecoilRoot>

        <CacheProvider value={emotionCache}>
          <Head>
            <meta name="viewport" content="initial-scale=1, width=device-width" />
          </Head>
          <ThemeProvider theme={theme}>
            {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
            <CssBaseline />
            <div style={{ visibility: mounted ? 'visible' : 'hidden' }}>
              {getLayout(<Component {...pageProps} />)}
            </div>
          </ThemeProvider>
        </CacheProvider>

      </RecoilRoot>
    </SessionProvider>
  )
}

and my index.tsx

import React, { ReactNode } from "react";
import type { Page } from 'next'
import { PageLayout } from "@app/UI/MUI/components/layout/page-layout";

const page: Page<{ someText: string }> = ({ someText }) => {
    return (
        <div>{someText}</div>
    )
}
export default page
page.getLayout = (page: Page) => {
    return (
        <PageLayout>{page}</PageLayout>
    )
}


export const getServerSideProps = async () => {

    return ({ props: { someText: 'This is a test' } })

}
  • Related