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
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' } })
}