【问题标题】:Persistent layout in Next.js with TypeScript and HOCNext.js 中的持久布局与 TypeScript 和 HOC
【发布时间】:2020-09-18 18:25:50
【问题描述】:

我想为我的 Next.js 应用程序的某些页面添加持久布局。我发现this article 解释了人们如何做到这一点的几种方法。看起来很简单,但是我在使用推荐的方法时遇到了以下两个问题:

  1. 我正在使用 TypeScript,但不知道如何输入。例如,我有以下,它正在工作,但我显然不喜欢使用as any
const getLayout =
    (Component as any).getLayout ||
    ((page: NextPage) => <SiteLayout children={page} />);
  1. 我正在使用 Apollo,因此我在某些页面上使用了 withApollo HOC(来自 here)。使用它会导致Component.getLayout 始终为undefined。我对正在发生的事情没有足够的了解,无法知道为什么会发生这种情况(我可以猜到),所以我自己很难解决这个问题。

【问题讨论】:

  • 你有没有找到比使用as any更好的方法?
  • 我没有@knite

标签: reactjs typescript next.js higher-order-components


【解决方案1】:

我有类似的问题,这就是我为我的项目解决的方法。

创建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 中切换类型以满足您的需求。

【讨论】:

    【解决方案2】:

    您可以通过将 getLayout 属性添加到 AppProps.Component 来扩展 Next 类型定义。

    next.d.ts(从下一个 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(从下一个 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 类型定义时,您必须提供准确的类型/接口签名(以及匹配的泛型),否则 declaration merging 将不起作用。不幸的是,如果库更改 API,这意味着调整类型定义。

    【讨论】:

      【解决方案3】:
      1. 我相信 next.js 提供了一个 Component 属性作为 AppProps 的一部分。 This example 应该有你要找的东西。
      2. 您可能需要在创建用withApollo 包裹的组件后分配getLayout
      import CustomLayout = CustomLayout;
      const MyPage = () => (...);
      
      const MyPageWithApollo = withApollo(MyPage);
      MyPageWithApollo.Layout = CustomLayout;
      export default MyPageWithApollo;
      

      此外,next.js 有两个示例可以用来实现这一点(虽然不是用 typescript 编写的):

      1. Dynamic App Layout
      2. With Apollo

      【讨论】:

      • 1.我知道有,但是Component 本身没有getLayout 函数,因此您需要更改其类型以使其与TypeScript 一起使用。不幸的是,您的示例毫无帮助。 2.同样的问题,因为它是TypeScript,你不能只做MyPageWithApollo.Layout,因为LayoutNextPage类型上不存在。
      猜你喜欢
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 2021-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多