

[teratailでも同様の質問](https://teratail.com/questions/300206)を投げましたが、2週間経過しても回答が0件のため、こちらでも質問させて頂きます。  
どちらに回答頂いても構いません。また進捗状況はどちらにも共有致します。よろしくお願い致します。

### 質問

Next.js + TypeScriptの勉強のため以下のリポジトリの改造を試みています。  
<https://github.com/jasonraimondi/nextjs-jwt-example>  
getServerSidePropsをComponentで使用する場合の書き方が分からず詰まってしまいました。

### 発生している問題・エラーメッセージ

web/components/private_route.tsx

[code]

    import ServerCookie from "next-cookies";
    import Router from "next/router";
    import React, { Component } from "react";
    import { COOKIES } from "../services/login_service";
    import { AuthToken } from "../services/auth_token";
    
    export type AuthProps = {
      auth: AuthToken
    }
    
    export function privateRoute(WrappedComponent: any) {
      return class extends Component<AuthProps> {
        static async getInitialProps(ctx: any) {
          const token = ServerCookie(ctx)[COOKIES.authToken];
          const auth = new AuthToken(token);
          const initialProps = { auth };
          if (auth.isExpired) {
            ctx.res.writeHead(302, {
              Location: "/login?redirected=true",
            });
            ctx.res.end();
          }
          if (WrappedComponent.getInitialProps) return WrappedComponent.getInitialProps(initialProps);
          return initialProps;
        }
    
        get auth() {
          // the server pass to the client serializes the token
          // so we have to reinitialize the authToken class
          //
          // @see https://github.com/zeit/next.js/issues/3536
          return new AuthToken(this.props.auth.token);
        }
    
        render() {
          return <WrappedComponent auth={this.auth} {...this.props} />;
        }
      };
    }
    
[/code]

を  
Next.js9.3以降ではgetInitialPropsではなくgetServerSideProps等が推奨されるとのことから、  
以下のような形に書き換えたいと思っています。

[code]

    import ServerCookie from "next-cookies";
    import Router from "next/router";
    import React, { Component } from "react";
    import { COOKIES } from "../services/login_service";
    import { AuthToken } from "../services/auth_token";
    import { GetServerSideProps } from "next";
    
    export type AuthProps = {
      auth: AuthToken
    }
    
    export function privateRoute(WrappedComponent: any) {
      return class extends Component<AuthProps> {
        // static async getInitialProps(ctx: any) {
        //   const token = ServerCookie(ctx)[COOKIES.authToken];
        //   const auth = new AuthToken(token);
        //   const initialProps = { auth };
        //   if (auth.isExpired) {
        //     ctx.res.writeHead(302, {
        //       Location: "/login?redirected=true",
        //     });
        //     ctx.res.end();
        //   }
        //   if (WrappedComponent.getInitialProps) return WrappedComponent.getInitialProps(initialProps);
        //   return initialProps;
        // }
    
        get auth() {
          // the server pass to the client serializes the token
          // so we have to reinitialize the authToken class
          //
          // @see https://github.com/zeit/next.js/issues/3536
          return new AuthToken(this.props.auth.token);
        }
    
        render() {
          return <WrappedComponent auth={this.auth} {...this.props} />;
        }
      };
    }
    
    export const getServerSideProps: GetServerSideProps = async (ctx : any) => {
          const token = ServerCookie(ctx)[COOKIES.authToken];
          const auth = new AuthToken(token);
          const initialProps = { auth };
          if (auth.isExpired) {
            ctx.res.writeHead(302, {
              Location: "/login?redirected=true",
            });
            ctx.res.end();
          }
          if (WrappedComponent.getInitialProps) return WrappedComponent.getInitialProps(initialProps);
          return initialProps;
    }
    
    
    
[/code]

実際に上記の通り書き換えたところ、  
(1) getServerSidePropsでreturnしているpropsをprivateRoute関数内で使用したいが、受け渡し方法が分からない。  
(2) if (WrappedComponent.getInitialProps) return
WrappedComponent.getInitialProps(initialProps);の「WrappedComponent」の部分で「名前
'WrappedComponent'
が見つかりません。」とシンタックスエラーが出る。またgetInitialPropsではなくgetServerSidePropsへの書き換え方が分からない。  
という問題が出て、詰まっています。  
(1)については、公式ドキュメントの解説どおり「function Page({ data })
{」のようにすべきかと思いましたが、このコードの場合、Componentで引数としてWrappedComponentを取るため、こういう場合どのように書けば良いのか分からない状態です。  
<https://nextjs.org/docs/basic-features/data-fetching#simple-example-1>  
ちなみにこちらのComponentは、  
web/pages/dashboard.tsx  
などで使用されており、ログイン時にのみ見れるページを作成する為のComponentになり、Pageのコードにて「export default
privateRoute(Page);」のような書き方で使用します。

### 質問

(1),(2)それぞれどのようにすれば解消出来るでしょうか。  
Next.js TypeScript共に初心者であるため、見当違いのことをしていれば申し訳ございません。ご指摘いただければ幸いです。

### 試したこと

getServerSidePropsに関して公式ドキュメントを読んだり  
GitHubで類似のコードを探したり書き方を数日模索しましたが、分かりませんでした。

### 補足情報（FW/ツールのバージョンなど）

Next.js 9.5  
TypeScript 3.5


CC BY-SA 4.0
2020-11-09T13:24:12.077
71827
2021-12-31T11:02:35.240
2020-11-10T09:44:13.047
32986
41817
question
javascript reactjs typescript next.js
Next.js+TypeScriptにてgetInitialPropsではなくgetServerSidePropsを使用したいが書き方が分からない。


  * `getServerSideProps`は`getInitialProps`と併用することが出来ません。  
(おそらく出来たとしても、２つのソースからのPropsのマージを考えることになり地獄っぽさが増します)

  * `getServerSideProps`(と `getInitialProps`)は`pages/`以下のファイルよりexportされたものが対象となるため、`components/`以下のファイルではハンドルされません。

  * `components/`以下にあるコンポーネントは、初期状態を`pages/`以下のPageコンポーネントからProps、ないしは状態管理フレームワークごしに受け取る形が必要になります。

    * `extjs-jwt-example`では`privateRoute()`で生成したComponentを`pages/`のファイルからdefault exportしているため、最終的に`pages/nantoka.tsx#default.getInitialProps`として解決されますが、編集後のファイルは`components/`のファイルから直接`getServerSideProps`をexportしているため、これはNext.jsから呼び出されません

という上で、私が考える妥当っぽい設計は以下のようなもんじゃないかなぁと考えます  
(ファイルの置き方はプロジェクトに合わせて適当にやってください)

  1. トークンの取得処理を関数に切り出す

[code]

    export const getOrForbiddenAuthToken = (ctx: GetServerSidePropsContext) => {
      const token = ServerCookie(ctx)[COOKIES.authToken]
      const auth = new AuthToken(token)
    
      if (auth.isExpired) {
        ctx.res.writeHead(302, {
          Location: '/login?redirected=true',
        })
        ctx.res.end()
    
        // 例外処理を雑にしているの問題ないか検証してください
        return null
      }
    
      return auth
    }
    
[/code]

  2. privateRoute関数を、server sideから降ってきたtokenからAuthTokenインスタンスを作って子供に渡すHigher Oreder Component化する

[code]

    // web/components/private_route.tsx
    
    export function privateRoute<P extends { auth: AuthToken }>(
      WrappedComponent: ComponentType<P>
    ) {
      // getServerSidePropsでシリアライズされたauthを受け取って
      return ({ auth, ...props }: { auth: AuthToken } & P) => {
        // コンポーネントのマウント時だけインスタンスを作る(useMemoについてはググってくれ)
        const token = useMemo(() => new AuthToken(auth.token), [auth])
    
        // AuthTokenのインスタンスをWrappedComponentに渡す (as any許してくれ)
        return <WrappedComponent auth={token} {...(props as any)} />
      }
    }
    
[/code]

  2. `pages/nantoka.tsx` のgetServerSidePropsでの2つ関数を呼び出し  
authとラップされたコンポーネントを得る

[code]

    // pages/nantoka.tsx
    import { getOrForbiddenAuthToken } from '~yourapp/auth'
    
    export const Page = privateRoute(({ auth }: { auth: AuthToken }) => {
      return <div>ここでなにかする</div>
    })
    
    export default Page
    
    export const getServerSideProps: GetServerSideProps = async (ctx) => {
      // getOrForbiddenAuthTokenを呼び出し、エラーレスポンスを返すとか色々させる
      const auth = getOrForbiddenAuthToken(ctx)
    
      // 認証でエラーが起きてもそれ以降(この行)まで処理が来るので
      /  認証していないといけない処理は if(auth != null) とか適当にガードしてください
      return { props: { auth } }
    }
    
[/code]

以上です〜


CC BY-SA 4.0
2021-01-24T04:29:18.210
73543
2021-01-24T04:29:18.210
43658
71827
answer