簡體   English   中英

Next.js 中的持久布局與 TypeScript 和 HOC

[英]Persistent layout in Next.js with TypeScript and HOC

我想為我的 Next.js 應用程序的某些頁面添加持久布局。 我發現這篇文章解釋了人們如何做到這一點的幾種方法。 看起來很簡單,但是我在使用推薦的方法時遇到了以下兩個問題:

  1. 我正在使用 TypeScript 並且不知道如何輸入它。 例如,我有以下工作,但我顯然不喜歡使用as any
const getLayout =
    (Component as any).getLayout ||
    ((page: NextPage) => <SiteLayout children={page} />);
  1. 我正在使用 Apollo,因此我在某些頁面上使用了withApollo HOC(來自此處)。 使用它會導致Component.getLayout始終為undefined 我對正在發生的事情沒有足夠的了解來知道為什么會發生這種情況(我可以猜到),所以我自己很難解決這個問題。

我有類似的問題,這就是我為我的項目解決它的方法。

創建一個types/page.d.ts類型定義:

import { NextPage } from 'next'
import { ComponentType, ReactElement, ReactNode } from 'react'

export type Page<P = {}> = NextPage<P> & {
  // You can disable whichever you don't need
  getLayout?: (page: ReactElement) => ReactNode
  layout?: ComponentType
}

在您的_app.tsx文件中,

import type { AppProps } from 'next/app'
import { Fragment } from 'react'
import type { Page } from '../types/page'

// this should give a better typing
type Props = AppProps & {
  Component: Page
}
const MyApp = ({ Component, pageProps }: Props) => {
  // adjust accordingly if you disabled a layout rendering option
  const getLayout = Component.getLayout ?? (page => page)
  const Layout = Component.layout ?? Fragment

  return (
    <Layout>
      {getLayout(<Component {...pageProps} />)}
    </Layout>
  )

  // or swap the layout rendering priority
  // return getLayout(<Layout><Component {...pageProps} /></Layout>)
}

export default MyApp

以上只是最適合我的用例的示例實現,您可以切換types/page.d.ts中的類型以滿足您的需求。

  1. 我相信 next.js 提供了一個Component道具作為 AppProps 的一部分。這個例子應該有你正在尋找的東西。
  2. 在創建用withApollo包裝的組件后,您可能需要分配getLayout
import CustomLayout = CustomLayout;
const MyPage = () => (...);

const MyPageWithApollo = withApollo(MyPage);
MyPageWithApollo.Layout = CustomLayout;
export default MyPageWithApollo;

此外,next.js 有兩個示例可以用來實現這一點(雖然不是用 typescript 編寫):

  1. 動態應用布局
  2. 與阿波羅

您可以通過將getLayout屬性添加到AppProps.Component來擴展 Next 類型定義。

next.d.ts (從 Next 11.1.0 開始):

import type { CompletePrivateRouteInfo } from 'next/dist/shared/lib/router/router';
import type { Router } from 'next/dist/client/router';

declare module 'next/app' {
  export declare type AppProps = Pick<CompletePrivateRouteInfo, 'Component' | 'err'> & {
    router: Router;
  } & Record<string, any> & {
    Component: {
      getLayout?: (page: JSX.Element) => JSX.Element;
    }
  }
}

pages/_app.tsx

import type { AppProps } from 'next/app';

const NextApp = ({ Component, pageProps }: AppProps) => {
  // Typescript does not complain about unknown property
  const getLayout = Component.getLayout || ((page) => page);
  // snip
}

如果您想擁有更多的類型安全性,您可以定義自定義NextPageWithLayout類型,該類型將斷言您的組件已設置getLayout屬性。

next.d.ts (從 Next 11.1.0 開始):

import type { NextPage } from 'next';
import type { NextComponentType } from 'next/dist/next-server/lib/utils';

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

pages/example-page/index.tsx

import type { NextPageWithLayout } from 'next';

const ExamplePage: NextPageWithLayout<ExamplePageProps> = () => {
  // snip
}

// If you won't define `getLayout` property on this component, TypeScript will complain
ExamplePage.getLayout = (page) => {
  // snip
}

請注意,在擴展 Next 類型定義時,您必須提供確切的類型/接口簽名(以及匹配的泛型),否則聲明合並將不起作用。 不幸的是,如果庫更改 API,這意味着調整類型定義。

// if no Layout is defined in page, use this
const Default: FC = ({children}) => <>{children}</>

function MyApp({Component, pageProps}: AppProps & {Component: {Layout: FC}}) {
  // this will make layput flexible. 
  // Layout is extracted from the page
  const Layout = Component.Layout ?? Default
  return (
      <Layout>
        <Component {...pageProps} />
      </Layout>
  )
}
export default MyApp

假設您要使用“主頁”中的Layout

import { Layout } from "@components"

export default function Home({
     ...
}
Home.Layout = Layout

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM