I am trying to use layout.tsx in the app directory of Next.js 13 to have a nav layout that is present on all the pages. I managed to make it so that when the user logs out/signs in, the navbar changes, but the issue is that I have to refresh the page (F5) to see the change on the navbar. I think this issue is related to the cache, and that's why I am trying to use export const dynamic = 'force-dynamic'.
I also added the client component to a server component because I thought that would be the issue, but it didn't solve the problem. I wanted to use export const dynamic = 'force-dynamic' to deal with the cache, but now I'm encountering an error that I can't seem to resolve. The error message I'm getting is:
Only async functions are allowed to be exported in a "use server" file.
And here is the detailed error trace:
./app/components/ClientInsideServerLayout.tsx
Error:
x Only async functions are allowed to be exported in a "use server" file.
,-[C:\Users\zantl\OneDrive\Documentos\GitHub\sssss\gestion-gastos-supabase\app\components\ClientInsideServerLayout.tsx:2:1]
2 |
3 | import { getSessionStatus } from "../ServerActions/isUserLoggedIn";
4 | import ClientLayout from "./ClientLayout"
5 | export const dynamic = 'force-dynamic'
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6 |
7 | export async function ClientInsideServerLayout() {
7 | const isLoggedIn = await getSessionStatus();
`----
Here's the code for each relevant file that's causing the error:
File: ClientInsideServerLayout.tsx
'use server';
import { getSessionStatus } from "../ServerActions/isUserLoggedIn";
import ClientLayout from "./ClientLayout"
export const dynamic = 'force-dynamic'
export async function ClientInsideServerLayout() {
const isLoggedIn = await getSessionStatus();
return (
<>
<ClientLayout isLoggedIn={isLoggedIn}></ClientLayout>
</>
)
}
File: ClientLayout.tsx
'use client'
import Link from 'next/link';
import { useRouter } from 'next/navigation'
export default function ClientLayout({ isLoggedIn }: { isLoggedIn: boolean }) {
const router = useRouter();
const signOut = async () => {
try {
const response = await fetch("http://localhost:3000/auth/sign-out", {
method: "POST"
});
if (response.ok) {
router.push("/")
console.log('The resource has been permanently moved.');
}
} catch (error: unknown) {
console.error('An error occurred:', error);
}
};
const logIn = () =>{
router.push("/login")
}
return (
<nav className="flex items-center justify-between flex-wrap bg-teal-500 p-6">
<div className="flex items-center flex-shrink-0 text-white mr-6">
{/* ... logo code ... */}
</div>
<div className="block lg:hidden">
{/* ... button code ... */}
</div>
<div className="w-full block flex-grow lg:flex lg:items-center lg:w-auto">
<div className="text-sm lg:flex-grow">
<Link href="/ingresos" passHref>
<span className="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white">
Ingresos
</span>
</Link>
</div>
<div>
{isLoggedIn ? (
<button onClick={signOut} className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0">Log Out</button>
) : (
<>
<button onClick={logIn} className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0 mr-2">Log In</button>
<button className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0">Sign Up</button>
</>
)}
</div>
</div>
</nav>
);
}
Here's the code for the RootLayout that I'm using:
// Existing RootLayout code
'use server';
import './globals.css';
import { getSessionStatus } from './ServerActions/isUserLoggedIn';
import { ClientInsideServerLayout } from './components/ClientInsideServerLayout';
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const isLoggedIn = await getSessionStatus();
return (
<html lang="en">
<body>
<div>
<ClientInsideServerLayout></ClientInsideServerLayout>
{children}
</div>
</body>
</html>
);
}
Here's the directory structure for context:
app/
auth/
callback/
route.ts
sign-in/
route.ts
sign-out/
route.ts
sign-up/
route.ts
components/
ClientInsideServerLayout.tsx
ClientLayout.tsx
Login.tsx
tablaIngresos.tsx
ingresos/
page.tsx
login/
messages.tsx
page.tsx
ServerActions/
isUserLoggedIn.ts
_examples/
client-component/
page.tsx
route-handler/
route.ts
server-action/
page.tsx
server-component/
page.tsx
auth-form.tsx
favicon.ico
globals.css
layout.tsx
page.tsx
components/
LogoutButton.tsx
NextJsLogo.tsx
SupabaseLogo.tsx
types/
supabase.ts
.env.local
.gitignore
middleware.ts
next-env.d.ts
next.config.js
package-lock.json
package.json
postcss.config.js
README.md
tailwind.config.js
tsconfig.json
Can someone explain why this error is happening, and how can I fix it while keeping the layout update functionality when clicking the log-out button? I want to achieve this without having to refresh the page.
Any insights or suggestions would be greatly appreciated! Thank you!
I had a similar issue in next 14 where I was using an index.ts file to centralize my exports of server actions. The issue was that I needed to remove the 'use server' from the index.ts file itself.
WRONG:
'use server'
/**
* Simplify and centralize server action exports
*/
export { signIn } from "./signIn";
export { signOut } from "./signOut";
export { createComment } from "./createComment";
export { createPost } from "./createPost";
export { createTopic } from "./createTopic";
CORRECT: Remove use server from top of file.
/**
* Simplify and centralize server action exports
*/
export { signIn } from "./signIn";
export { signOut } from "./signOut";
export { createComment } from "./createComment";
export { createPost } from "./createPost";
export { createTopic } from "./createTopic";
Example of createComment server action
"use server";
export async function createComment() {}
Properly exporting an async function from it with use server at the top of each individual server action file.
Hope this helps someone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With