Next.js 備忘録: useContext を使った状態管理とサーバーサイドの制約に悩んだ話

## はじめに
Next.js を使っていると、サーバーサイドとクライアントサイドの違いや、それぞれの制約に引っかかることが多いです。特に、状態管理で useContext を使おうとしたとき、サーバーサイドのファイルとどう整合性を取るべきかで沼りました。この記事は、そんな状況を整理して備忘録的にまとめたものです。


## 開発中に直面した悩み
**1:**  `useContext` で`Header.tsx`と`page.tsx`の状態を共有したい
**2:** サーバーサイドで `metadata` を使って SEO 情報を設定しているため、`RootLayout`をサーバーサイドにしておきたい。

## なぜ困ったのか

`metadata` はサーバーサイドで生成される必要があるので、`RootLayout` で `use client` をつけられません。でも、`RootLayout` で全体の状態を管理しようと思って `useContext` を使いたい場合、`useState` などの React Hooks を使えないのがネックでした。



## 問題のコード
```react:qiita.rb

const metadata = {
  title: 'My App',
  description: 'this is Next.js app',
};



export default function RootLayout({ children }) {
  const [state, setState] = useState('');
  const stateInfo = {
    state,
    setState,
  };
  const AppStateContext = createContext(stateInfo);


  return (
    <html>
      <AppStateContext.Provider value={AppStateContext}>
        <body>{children}</body>
      </AppStateContext.Provider>
    </html>
  );
}

```
しかし`use client` をつけると `metadata` が使えなくなる。

## 解決策: 状態管理を分離する
結論として、状態管理は `RootLayout` ではなく、別のクライアントサイドの`Provider`コンポーネントを作成して、任せることにしました。


### 実装例
状態管理用の `Provider` を作る
```react:qiita.rb
'use client';
import React, { createContext, ReactNode, useState } from 'react';

export const AppStateContext = createContext<ProviderContext | undefined>(
  undefined
);

type ProviderContext = {
  state: number; 
  setState: React.Dispatch<React.SetStateAction<number>>;
};

export function Provider({ children }: { children: ReactNode }) {
  const [state, setState] = useState(0);

  const stateObject = {
    state,
    setState,
  };

  return (
    <AppStateContext.Provider value={stateObject}>
      {children}
    </AppStateContext.Provider>
  );
}
export default Provider;
```
### RootLayout に組み込む
```react:qiita.rb
import { AppStateProvider } from './Provider';

export const metadata = {
  title: 'My App',
  description: 'A simple Next.js app',
};

export default function RootLayout({ children }) {
  return (
    <html lang='en'>
      <Provider>
        <body
          className={`${geistSans.variable} ${geistMono.variable} antialiased`}
        >
          <Header />
          {children}
        </body>
      </Provider>
    </html>
  );
}
```

### `Page` での利用例

```react:qiita.rb
'use client';
import React, { useContext } from 'react';
import { AppStateContext } from '../Provider';

const Page = () => {
  const providedObject = useContext(AppStateContext);
  const plus = () => {
    providedObject?.setState((prev) => prev + 1);
  };
  return (
    <div>
      <h1>Aのページ</h1>
      <h1>{providedObject?.state}</h1>
      <button onClick={plus}>plus</button>;
    </div>
  );
};

export default Page;
```

## `Header` での利用例
```react:qiita.rb
'use client';
import React, { useContext } from 'react';
import { AppStateContext } from './Provider';

const Header = () => {
  const providedObject = useContext(AppStateContext);
  const plus = () => {
    providedObject?.setState((prev) => prev + 1);
  };
  return (
    <>
      <h1>{providedObject?.state}</h1>
      <button onClick={plus}>plus</button>;
    </>
  );
};

export default Header;
```

### 結果
`Header.tsx` と `page.tsx` の両方から `state` を操作でき、どちらの操作もリアルタイムに反映されました。

## まとめ
専用の Provider を作ることで、状態管理を分離し、サーバーサイドとクライアントサイドの制約を回避できた

## 終わりに

この記事は、私自身の学習過程での気づきを整理した備忘録です。
もし内容に誤りや補足すべき点があれば、ぜひコメントで教えていただけると幸いです。

##### 参考記事
https://future-architect.github.io/articles/20231214a/

https://ja.react.dev/reference/react/useContext