1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

typescript + nextjsをvercelにデプロイしたところ、ReactのuseContextまわりの扱いでエラーに苦しんだので書きます

環境

  • MacOS - AppleM1チップ - 16GBメモリ - Sonoma14.5
  • npm - v10.8.0
  • React - v^18.0
  • Nextjs - v14.2.4

Vercel - 発生したエラー

Buildでこちら2つのエラーが発生しました。

Build Logs
- Error:
- x You're importing a component that needs createContext. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
Build Logs
- Failed to compile.
  app/page.tsx
- Type error: Page "app/page.tsx" does not match the required types of a Next.js Page.
- "Context" is not a valid Page export field.

ルートページのtsxは以下のようにしていました

app/page.tsx
import { createContext, useState } from 'react';
import { Header, MainBody, ...} from '@components';
import { contextConfig, contextData } from 'context';

export const Context = createContext<contextConfig>({
    context_data: {
        // グローバルStateの変数の型
    } as contextData,
    setContextData: () => {}
});

export default function Home() {
    const [context_data, setContextData] = useState({
        // グローバルStateの変数の型
    } as contextData);

    return (
        <Context.Provider value={{context_data, setContextData}}>
            <Header />
            <MainBody>
                ...
            </MainBody>
        </Context.Provider>
    );
}

エラー1: 解決策 - use client

"use client"を追記する

  • Nextjsでコンポーネント間の依存関係(propsuseContextでの変数の受け渡しなど)が発生する場合、クライアント側での実行に制御する必要があります
  • "use client"が書かれたコンポーネントの子コンポーネントは全てクライアントで制御されることになるので、"use server"も駆使する場合は注意が必要

createContextの定義元で"use client"を追記してやる

app/page.tsx
+ "use client"
import { createContext, useState } from 'react';
import { Header, MainBody, ...} from '@components';
import { contextConfig, contextData } from 'context';

export const Context = createContext<contextConfig>({
...

これで1つ目のエラーは解消されました。

エラー2: 解決策 - Provider

ページコンポーネントとProviderは分ける

  • Vercel環境でのNextjsは、app以下のルーティング専用のtsxは、ページコンポーネント以外の型のexportを受け付けないようです
  • app/page.tsxでは、Contextオブジェクトもexportしてしまっているので、エラーが発生していました

Provierコンポーネントとして分けてやる

provider.tsx
import { createContext, useState } from 'react';
import { contextConfig, contextData } from 'context';

export const Context = createContext<contextConfig>({
   context_data: {
        // グローバルStateの変数の型
    } as contextData,
   setContextData: () => {}
});

const Provider: React.FC<PropsWithChildren> = ({children}) => {
    const [context_data, setContextData] = useState({
        // グローバルStateの変数の型
   } as contextData);

   return (
       <Context.Provider value={{context_data, setContextData}}>
           {children}
       </Context.Provider>
   );
}

export default Provider;
app/page.tsx
import { Header, MainBody, ...} from '@components';
+ import Provider from '@components/provider';

export default function Home() {
    return (
+        <Provider>
            <Header />
            <MainBody>
                ...
            </MainBody>
+        </Provider>
    );
}

これで2つ目のエラーも解消されました

補足

React - useContext

  • グローバルStateを扱うhook
  • useStateは単一コンポーネント内でしか扱えず、子コンポーネントで使用する場合はpropsで渡す必要がある
  • 深いネスト関係にあるコンポーネントでは、最上部から最下部に変数を渡したいとしても、ネストを辿ってpropsで渡し続けるしかない
  • useContextで作られた<context.Provider />を使えば、Providerの子コンポーネントはどの位置であっても変数を使用&更新できる
component.tsx
import { createContext, PropsWithChildren, useState } from 'react';

// グローバルStateの変数の型
type contextData = {
    prop1: string,
    prop2: number,
    prop3: string[]
}


// グローバルStateの変数と更新関数の型
type contextConfig = {
    context_data: contextData
    setContextData: React.Dispatch<React.SetStateAction<contextData>>
}

// Contextの作成
export const Context = createContext<contextConfig>({
    context_data: { // デフォルト値
        prop1: '',
        prop2: 0,
        prop3: []
    } as contextData,
    setContextData: () => {}
});

// Context.provider
const Provider: React.FC<PropsWithChildren> = ({children}) => {
    const [context_data, setContextData] = useState({
        prop1: '',
        prop2: 0,
        prop3: []
    } as contextData);

    return (
        <Context.Provider value={{context_data, setContextData}}>
            {children}
        </Context.Provider>
    );
}

export default Provider;
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?