teratail header banner
teratail header banner
質問するログイン新規登録

質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.30%
Next.js

Next.jsは、Reactを用いたサーバサイドレンダリングなどを行う軽量なフレームワークです。Zeit社が開発しており、nextコマンドでプロジェクトを作成することにより、開発環境整備が整った環境が即時に作成できます。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

2回答

303閲覧

Next.jsでuseContextを使用した際のエラー解消方法について

KAI

総合スコア9

Next.js

Next.jsは、Reactを用いたサーバサイドレンダリングなどを行う軽量なフレームワークです。Zeit社が開発しており、nextコマンドでプロジェクトを作成することにより、開発環境整備が整った環境が即時に作成できます。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2025/01/08 02:42

0

0

実現したいこと

Next.jsのルートコンポーネント(page.js及びlayout.js)をパフォーマンス向上の為SSRにしたいです。

layout.jsをSSRにしながら、state等を利用するような方法が知りたいです。

発生している問題・分からないこと

元々layout.jsでuseStateやuseEffectを使用していたので外部コンポーネントに移すようにしました。
AIに聞きながら進めましたが、私の理解が浅いせいかむしろ状況が複雑になってしまいました。
カスタムフックにすれば問題ないと提案され実装→CSRにしないとエラーになることが判明

useContextで管理すれば問題ないと提案され実装(念入りに確認)→恐らくですが、、CSRにしないとエラーになりそうです。

コード等は本件に関係しそうな一部のみ公開していますので、追加の情報が必要な場合はお申し付けください。

エラーメッセージ

error

1Error: (0 , react__WEBPACK_IMPORTED_MODULE_4__.useContext) is not a function

該当のソースコード

layout.js

1import "./globals.css"; 2import Header from './Components/Header/Header'; 3import { Provider } from 'react-redux'; 4import store from './Redux/store'; 5import { useContext } from "react"; 6import { LayoutStateContext, LayoutStateProvider } from "./Components/Hokks/LayoutContext"; 7 const Layout = ({ children }) => { 8 const { currentPage, isMounted } = useContext(LayoutStateContext); 9 10 if (!isMounted) { 11 return null; 12 } 13 14 return ( 15 <html lang="ja"> 16 <body className='max-w-screen-2xl mx-auto h-full w-full scroll-smooth'> 17 <Provider store={store}> 18 <LayoutStateProvider> 19 <Header currentPage={currentPage} /> 20 {children} 21 </LayoutStateProvider> 22 </Provider> 23 </body> 24 </html> 25 ) 26 } 27export default Layout

LayoutContext.js

1"use client"; 2import { usePathname } from 'next/navigation'; 3import { createContext, useContext, useEffect, useState } from 'react' 4 5const LayoutStateContext = createContext(); 6 7 const LayoutStateProvider = ({ children }) => { 8 const [currentPage, setCurrentPage] = useState(""); 9 const [isMounted, setIsMounted] = useState(false); 10 const path = usePathname(); 11 12 useEffect(() => { 13 setCurrentPage(path === "/" ? "" : "Chat"); 14 setIsMounted(true); 15 }, [path]); 16 17 return ( 18 <LayoutStateContext.Provider value={{ currentPage, isMounted}}> 19 {children} 20 </LayoutStateContext.Provider> 21 ); 22}; 23 24export { LayoutStateContext, LayoutStateProvider };

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

解決できず、良い解消法も思い浮かばずです。

補足

バージョン
next :^14.2.20
react:^18.3.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

Next.jsのルートコンポーネント(page.js及びlayout.js)をパフォーマンス向上の為SSRにしたいです。

以下のような理由から、一旦は「パフォーマンス向上のための Server Component への切り替え」は諦めて、必要な layout / page に "use client" を付けて回るべきだと思います。

「早すぎる最適化は諸悪の根源」という格言があります。正しい書き方ができないうちから「最適化のために〇〇すべきらしい」という噂に流されず、まずは「正しい書き方で、動くもの」を作るべきです。


1. 「Client Component だから SSR されない」は誤り

まず、「Client Component だから SSR されない」と思っていらっしゃるとしたら、それは誤りです。

Server Component (以下 SC)は、getServerSideProps と同様に「SSR のための、前段階の処理」であるに過ぎません。

Client Component (以下 CC)は Pages Router のときも、App Router であっても変わらず、(何もしない限りデフォルトで)SSR されます。

https://zenn.dev/yumemi_inc/articles/use-client-directive-explained-with-gssp

2. 「パフォーマンスのために SSR にする」の解釈のしかたの誤り

あと、巷で言われている「CSR が重いから、SSR にすべき」というのを、App Router に正しく翻訳すると、

  • 「CSR が重い」
    • → データの取得を CC の中で行うのは、パフォーマンスが悪くなる
  • 「SSR にすべき」
    • → データの取得を SC の中で行うほうが、パフォーマンスが良くなる
      • 取得したデータは、その中の CC, SC のどちらに渡しても問題ない(特別な対応をしなければ、SSR される)

です。

3. まとめ

なので、無理に app/layout.js を SC 化する必要はありません。

各ページ app/hogehoge/page.js を、"use client" なしで書けば、それは SC になり(ここも、誤解なさっている可能性がありますが、重要なところです)、その中で取得したデータを、ページ内の各部分に配分すれば良いのです。

その「ページ内の各部分」は、SC であっても CC であっても、特に問題ありません。

以下のように書けます!

books/page.js

1// "use client" が無いので、SC 2import { BookList } from "./book-list.js"; 3import { getBooks } from "./get-books.js"; 4 5const BooksIndexPage = async () => { 6 const books = await getBooks(); 7 return ( 8 <div> 9 <BooksFilter /> 10 <BooksList items={books} /> 11 </div> 12 ); 13} 14export default BooksIndexPage;

books/book

1"use client" 2export const BookList = () => { // 以下略

投稿2025/01/11 02:00

編集2025/01/11 02:06
honey32

総合スコア246

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KAI

2025/01/11 04:15

とても分かり易いご回答ありがとうございます。 初心者なので、SSR=早い,CSR=遅いぐらいの理解しかできていなかったのでとても参考になりました。 処理が遅い理由についても、下記部分でハイドレーションエラーのようになっていたのが原因だったようで if (!isMounted) {return null;} この箇所を削除しただけでだいぶ早くなりました ※isMounted自体ほぼ記述意味のないコードだと後で判明しました。 特に状態を渡したりもしてないので、途中で放置してしまったコードなのかなと思います。
honey32

2025/01/11 10:22

お力になれたなら幸いです!
guest

0

layout.jsで以下のようにhooksを利用している以上は、クライアントコンポーネントになってしまいます。

layout.js

1 const { currentPage, isMounted } = useContext(LayoutStateContext);

Reactのhooksはブラウザで動作するために作られているものですので、サーバーコンポーネントでは利用できません。

ですので、以前の質問でも提案させていただきましたが、NextjsのRenderに関するドキュメントをお読みいただいて理解いただく必要があるかと思います(NextのRenderに関するドキュメント)。

投稿2025/01/08 03:03

uky

総合スコア270

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KAI

2025/01/08 05:22

ご回答ありがとうございます。 少し読んでみました。 現在の私ではそこまで完璧に理解するのは難しいですが、 ルートコンポーネント(layout.js)をCSRにしてしまう事も考慮しなくてはいけないようですね
uky

2025/01/08 08:44

そうですね、質問に掲載いただいている通り、layout.js内でuseContextを使用したい場合ですと、clientコンポーネントにせざるを得ないと思います。 どうしてもlayout.jsをサーバーコンポーネントにしなければならない理由があるのであれば、 useContextを呼び出すためのクライアントコンポーネントを作成し、Compositionパターンを使って呼び出してあげるなどの対応が必要になります。(Compositionパターンの説明には「NextのRenderに関するドキュメント」をクリックいただければすぐに遷移できます)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問