Pages RouterからApp Routerへ移行した話 Next.js v13 

## この記事は
Next.js v13からApp RouterがStableになった事に伴って、PagesからAppへの移行作業を**ミニマム**で行ったので、その備忘録です。

これから移行作業を行うという方は、必ず[公式ドキュメント](https://nextjs.org/docs/app)を読んだ上で、一例としてこういう作業があるんだ〜くらいの所感で読んでいただけると幸いです🙇‍♀️

**環境**
```json
"next": "13.4.2"
"typescript": "4.9.4"
"@chakra-ui/react": "2.5.5"
```

## 基本的な変更
- **ページコンポーネントは、ファイル名を`page.tsx`にする事で認識される**
`index.tsx` → `page.tsx`
`edit.tsx` → `edit/page.tsx`

- **client sideのコンポーネントはファイルの一番上に`use client`を記述する**
・ファイル内でHooksを使っているなら必須です


- **_app.tsxはapp/layout.tsxで**
rootの`layout.tsx`ファイルは必須！ 
Custom Appが使えない →AppPropsは使えず、ReactNodeしかchildrenに渡せない

`_app.tsx`
```tsx
const App = ({ Component, pageProps }: AppProps) => {
  return (
      <ChakraProvider theme={theme}>
        <Container>
          <Component {...pageProps} />
        </Container>
      </ChakraProvider>
  );
};
```

`layout.tsx`
```tsx
const RootLayout: FC<{ children: ReactNode }> = ({ children }) => {
  return (
    <html lang="ja">
      <body>
        <ChakraProvider theme={theme}>{children}</Container>
      </body>
    </html>
  );
};
```
（ `<Container>`はrootの`page.tsx`に移動）

>layoutの場合、親レイアウトとその子コンポーネントの間でデータを渡すことはできません。ルート内で同じデータを複数回リクエストする場合でも、データを必要とするレイアウト内で直接データを取得することをお勧めします。裏側では、ReactとNext.jsがリクエストをキャッシュし、重複排除することで、同じデータが複数回フェッチされるのを防ぎます。

https://nextjs.org/docs/app/building-your-application/data-fetching#fetching-data-at-the-component-level

- もしclientで動くproviderがあるなら、layout.tsxに”use client”をつけてあげるか、別のファイルに”use client”をつけたprovider.tsxを作って切り出してあげて、importしてくる

## useRouter
- **import先が`next/router` → `next/navigator`**
useRouterでqueryが取れなくなった
pushで引数に渡せる型がstringだけ

- **params：`useParams()`**（v13.3から）
```tsx
const params = useParams();
const id = params?.id;
```

- **query：`useSearchParams()`**
```tsx
const searchParams = useSearchParams();
const id = searchParams?.get("id");
```

- **paramsとsearchParamsはPage componentsで取ってこれる**
```tsx
const Page = ({
  params,
  searchParams
}: {
  params: {
    eventId: string;
  };
  searchParams: {
	userId: string;
  };
}) => {
  const eventId = params.eventId;
  const userId = searchParams.userId
```
ただし、`searchParams`はpage.tsxでしか使えない

## metadata API
- **`next/head`は廃止**
今までは`next/head`から`<Head>`をimportして定義していた
　→同じ階層に`head.tsx`か`page.tsx`でmetadataを定義
```tsx
export const metadata: Metadata = {
  title: '...',
  description: '...',
}
```

- **generateMetadata**
generateMetadataは引数にparams・searchParamsを持ってこれる（dynamic defind）
ただし、**generateMetadataを使えるのはserver component(server side)だけ**
```ts
type Props = {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
};

export async function generateMetadata(
  { params, searchParams }: Props,
): Promise<Metadata> {
  const id = params.id;
 
  const product = await fetch(`https://.../${id}`).then((res) => res.json());
 
  return {
    title: product.title,
  };
}
```
ただし、`searchParams`を持てるのは、`page.tsx`だけ（`layout.tsx`では`params`のみ使用可能）
>Page
>Page(Server Components)で検索パラメータにアクセスするには、searchParams プロパティを使用します。
>
>Layout
Pageとは異なり、Layout(Server Components)はsearchParamsプロップを受け取りません。これは、共有レイアウトがナビゲーションの間に再レンダリングされないため、ナビゲーションの間にsearchParamsが古くなる可能性があるためです。
>
>代わりに、PageのsearchParamsまたはClient ComponentsのuseSearchParams hooksを使用してください。

https://nextjs.org/docs/app/api-reference/functions/use-search-params#server-components

## Chakra ui
- **`CacheProvider`を使ってあげる**
詳細は[公式ドキュメント](https://chakra-ui.com/getting-started/nextjs-guide#app-directory-setup)

## その他
### `useSelectedLayoutSegment()`
- ページののpathNameをstringで取得できる

### `useSelectedLayoutSegments()`
- 階層構造を配列で取得できる → breadcrumbsとかに便利

### API 
- ファイル名をroute.tsにすることで、apiとして認識される

## 最後に
始めにも書きましたが、これは移行の一例です
Next.jsは日に日にアップデートされるので、常に公式ドキュメントを読むことを心がけていきたいところです

最後まで読んでくださりありがとうございました！