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

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

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

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

tailwindcss

tailwindcssとは、Webサイト・Webアプリケーションを作成するためのCSSフレームワークの一つ。ユーティリティファーストな点が特徴です。汎用的なクラスが多数用意されており、独自のデザインを自由に組み立てることができます。

Q&A

解決済

1回答

106閲覧

Rootlayoutにてページの遷移毎に値(session, pathname)を更新できるようにしたい

gakut

総合スコア14

Next.js

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

tailwindcss

tailwindcssとは、Webサイト・Webアプリケーションを作成するためのCSSフレームワークの一つ。ユーティリティファーストな点が特徴です。汎用的なクラスが多数用意されており、独自のデザインを自由に組み立てることができます。

0グッド

2クリップ

投稿2025/04/01 05:29

編集2025/04/01 15:07

実現したいこと

App routerを用いたnext.jsプロジェクトのRootlayout(サーバサイド)で用いる値(session, pathname)を、ページの遷移毎に更新したい。

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

page.tsxを読み込んでもlayout.tsxはリロードしないので、値は自動的に更新されません。
RootlayoutではgetServerPropsが使えず、サーバサイドなのでフックも使えないので、それ以外の方法を探しているのですが見つかりません。
(getServerSessionもheadersもプロジェクトの開始時かリロード時に実行されるので、流石に毎回リロードするのはUX的にNGかなと思います。)

該当のソースコード

layout.tsx

1import './globals.css'; 2 3import { Metadata } from 'next'; 4// googleのフォントの一種であるInconsolataをインポートしている 5import { Inconsolata } from 'next/font/google'; 6 7import { headers } from 'next/headers'; 8 9import SignOutMenu from '@/components/SignOutMenu'; 10 11import Link from 'next/link'; 12import Image from 'next/image'; 13 14import { getServerSession } from 'next-auth'; 15import { authOptions } from '@/lib/authOptions'; 16 17const fnt = Inconsolata({ subsets: ['latin']}); 18 19export default async function RootLayout({children,}: Readonly<{ 20 children: React.ReactNode; 21}>) { 22 const session = await getServerSession(authOptions); 23 const pathname = (await headers()).get("x-url") || ''; 24 25 return ( 26 <html lang="ja"> 27 <body className={fnt.className}> 28 <h1 className="text-4xl text-indigo-800 font-bold my-2">Earthlete</h1> 29 <ul className="flex bg-blue-600 mb-4 pl-2"> 30 <li className={`block px-4 py-2 my-1 hover:bg-gray-100 rounded ${pathname === '/' ? "bg-fuchsia-600" : ""}`}> 31 <Link className="no-underline text-blue-300" href="/"> 32 ホーム</Link></li> 33 <li className={`block text-blue-300 px-4 py-2 my-1 hover:bg-gray-100 rounded ${pathname === '/threads' ? "bg-fuchsia-600" : ""}`}> 34 <Link className="no-underline text-blue-300" href="/threads"> 35 掲示板</Link></li> 36 </ul> 37 <ul className="flex fixed top-3 right-2 z-20"> 38 {pathname !== '/logIn' && !session && 39 <li className="block px-3 py-1 my-1 hover:bg-gray-100 rounded"> 40 <Link className="no-underline text-blue-600" href="/signIn"> 41 サインイン</Link></li> 42 } 43 <SignOutMenu pathname={pathname} login={session ? true : false}/> 44 {pathname !== '/addAccount' && !session && 45 <li className="block px-3 py-1 my-1 hover:bg-gray-100 rounded"> 46 <Link className="no-underline text-orange-400" href="/addAccount"> 47 新規登録</Link></li> 48 } 49 {pathname !== '/editProfile' && session && 50 <li className="px-1 py-1 mx-2 rounded-full border"> 51 <Link className="text-blue-300" href="/editProfile"> 52 <Image src='/defaultIcon.png' alt="" width={32} height={32}/> 53 </Link></li> 54 } 55 </ul> 56 <div className="mx-2"> 57 {children} 58 </div> 59 </body> 60 </html> 61 ); 62}

SignOutMenu.tsx

1'use client'; 2 3import Link from 'next/link'; 4 5import { useState } from 'react'; 6 7import { signOut } from 'next-auth/react'; 8import { redirect } from 'next/navigation' 9 10export default function SignOutMenu({ pathname, login } : { pathname: string | null, login: boolean }) { 11 const [showLogoutMenu, setShowLogoutMenu] = useState<boolean>(false); 12 13 const handleLogout = () => { 14 signOut(); 15 setShowLogoutMenu(false); 16 redirect('/'); 17 }; 18 19 return ( 20 <> 21 {showLogoutMenu && 22 <div className="fixed top-0 left-0 bg-gray-300 opacity-50 h-full w-full z-21"></div> 23 } 24 {pathname !== '/logOut' && login && 25 <li className="block px-3 py-1 my-1 hover:bg-gray-100 rounded"> 26 <button className="text-blue-600" onClick={() => setShowLogoutMenu(!showLogoutMenu)}> 27 サインアウト</button></li> 28 } 29 {showLogoutMenu && login && 30 <div className="fixed flex top-0 left-0 justify-center items-center text-center w-full h-screen z-22"> 31 <div className=" border-black border-2 w-3/5 md:w-2/5 lg:w-1/5 h-1/5"> 32 <p className="my-6">ログアウトしますか?</p> 33 <ul className="flex justify-around"> 34 <li><Link className="no-underline text-white bg-blue-600 p-4 rounded" onClick={ () => setShowLogoutMenu(false) } href={pathname || "/"}> 35 キャンセル</Link></li> 36 <li><Link className="no-underline text-white bg-red-600 p-4 rounded" onClick={handleLogout} href="/"> 37 サインアウト</Link></li> 38 </ul> 39 </div> 40 </div> 41 } 42 </> 43 ); 44}

試したこと・調べたこと

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

上述の通り、getServerPropsを使おうとしても、これはpage.tsxにしか使えないので問題は解決しませんでした。(page.tsxにコンポーネントは配置したくないです。)

補足で、getServerSessionに関してはsignOutした時は再レンダリングされてサインアウト後の状況が反映されているのですが、signInの時はなぜか再レンダリングされずサインイン後の状況が反映されないことが確認できました。

更に補足で、signInのオプションであるredirect: falseを消去したら再レンダリングされてgetServerSessionが反映されるようになりました!!

あとはheaders()の方だけ解決できたらOKです!!

補足

signOutMenuコンポーネントとlayout.tsx内のコンポーネントの背景色や表示/非表示を現在のurl(pathname)とログイン状況(session)で制御しています。

あと、rootlayoutをサーバサイドにすることにこだわっているのは、app routerのメリットを活かすためです。もし、この点を気にしなくても良いと分かれば、rootlayoutはクライアントサイドにしようと思います(https://zenn.dev/h_tatsuru/articles/app_router_pros_and_cons)。

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

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

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

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

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

guest

回答1

0

自己解決

結局、layout.tsxのリンクコンポーネントをSignOutMenu.tsxに移した上で、SignOutMenu.tsxにてusePathnameでpathnameを取得して、リンクコンポーネントの状態を変更するようにしました!!
middleware (headers()) でリロードしようとしても出来ない上に、したとしても毎回ページを遷移するたびに全て再レンダリングするのは非効率なので、ayout.tsxにてgetServerSessionで取得したsessionの有無をSignOutMeni.tsxコンポーネントに渡して、この有無と先述のpathnameを使ってリンクの状態を変更するようにしました!!

投稿2025/04/01 16:01

gakut

総合スコア14

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.32%

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

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

質問する

関連した質問