Functions cannot be passed directly to Client Components
Unanswered
Da_v_id posted this in #help-forum
Da_v_idOP
export default async function RootLayout({
children,
params: { locale },
}: Readonly<{
children: React.ReactNode;
params: {
locale: Locale;
};
}>) {
const translatedData = await getTranslatedData(locale);
return (
<html lang={locale} suppressHydrationWarning>
<body
className={cn(
"min-h-screen bg-background font-sans antialiased max-w-2xl mx-auto py-12 sm:py-24 px-6",
fontSans.variable
)}
>
<TranslatedDataProvider translatedData={translatedData}>
<ThemeProvider attribute="class" defaultTheme="light">
<TooltipProvider delayDuration={0}>
{children}
<Navbar />
</TooltipProvider>
</ThemeProvider>
</TranslatedDataProvider>
</body>
</html>
);
}how come as soon as I add
await to getTranslatedData which is flagged with "use server" I immediately get this error:Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server". Or maybe you meant to call this function rather than return it.
{$$typeof: ..., render: function Home}if I remove it, it works but what is the point of having a promise in the context provider?
74 Replies
@Da_v_id you can't pass function directly to the client component. what you can pass to the client component server actions
the correct way to do this is to make a client component with all of your providers
Da_v_idOP
I understand but where in this code I am passing a function to a client component? If I wait for the promise i should just pass the resulting string, right?
the problem arise when i put the 'await' in front of my promise
can you show me the
TranslatedDataProvider component?Da_v_idOP
here:
"use client"
import { getTranslatedData } from "@/data/resume"
import React from "react"
type TranslatedData = ReturnType<typeof getTranslatedData>
const TranslatedDataContext = React.createContext<TranslatedData | null>(null)
export default function TranslatedDataProvider({
translatedData,
children,
}: {
translatedData: TranslatedData
children: React.ReactNode
}) {
return (
<TranslatedDataContext.Provider value={translatedData}>
{children}
</TranslatedDataContext.Provider>
)
}
export function useTranslatedData() {
const translatedData = React.useContext(TranslatedDataContext)
if (translatedData === null) {
throw new Error('useTranslatedData hook must be used within TranslatedDataProvider')
}
return translatedData
}I also tried to play around with
TranslatedData type and put Awaited<ReturnType<typeof getTranslatedData> when I wait for the data in the layoutimport { getTranslatedData } from "@/data/resume"what's this line for?
Da_v_idOP
oh it's just used for the type
wait @Da_v_id do you have "use server" in
getTranslateData function?show me that function
import { getTranslatedData } from "@/data/resume"show me this file
Da_v_idOP
i do have and it looks like this:
and
"use server";
import { getDictionary } from '@/lib/getDictionaries';
export const getTranslatedData = async (locale: Locale) => {
const intl = await getDictionary(locale);
const getDescription = (descriptions: MultiLanguage) => {
return descriptions[locale] || descriptions['en'];
};
return ({...})and
getDictionary:import 'server-only'
import { i18n, Locale } from '@/lib/i18-config'
const dictionaries = {
it: () => import('@/dictionaries/it.json').then((module) => module.default),
en: () => import('@/dictionaries/en.json').then((module) => module.default),
}
export const getDictionary = async (locale: Locale) => dictionaries[i18n.locales.includes(locale) ? locale : i18n.defaultLocale]()okay so @Da_v_id "use server" is used wrongly
"use server" is like a door to the server from the client side
it should be used for server actions
Da_v_idOP
right, I have actually played around with that too, and if i remove it the result will be the same
okay anyway, we should remove "use server"
and you still get the same error?
Da_v_idOP
done, thank you!
can you create a separate typescript file for the interfacecs and types for the function and import types from there?
@Da_v_id
@James4u can you create a separate typescript file for the interfacecs and types for the function and import types from there?
Da_v_idOP
Sure, which interfaces are you referring to?
@Da_v_id here:
ts
"use client"
import { getTranslatedData } from "@/data/resume"
import React from "react"
type TranslatedData = ReturnType<typeof getTranslatedData>
const TranslatedDataContext = React.createContext<TranslatedData | null>(null)
export default function TranslatedDataProvider({
translatedData,
children,
}: {
translatedData: TranslatedData
children: React.ReactNode
}) {
return (
<TranslatedDataContext.Provider value={translatedData}>
{children}
</TranslatedDataContext.Provider>
)
}
export function useTranslatedData() {
const translatedData = React.useContext(TranslatedDataContext)
if (translatedData === null) {
throw new Error('useTranslatedData hook must be used within TranslatedDataProvider')
}
return translatedData
}
you are basically importing
getTranslatedDatain the client component for only typesso define types in a separated typescript file and try to import it
@James4u you are basically importing `getTranslatedData`in the client component for only types
Da_v_idOP
I can replace it with
any for nowtry it then, remove the import line
Da_v_idOP
like this:
I get the same error :/
"use client"
import type { getTranslatedData } from "@/data/resume"
import React from "react"
type TranslatedData = ReturnType<typeof getTranslatedData>
const TranslatedDataContext = React.createContext<TranslatedData | null>(null)
export default function TranslatedDataProvider({
translatedData,
children,
}: {
translatedData: TranslatedData
children: React.ReactNode
}) {
return (
<TranslatedDataContext.Provider value={translatedData}>
{children}
</TranslatedDataContext.Provider>
)
}
export function useTranslatedData() {
const translatedData = React.useContext(TranslatedDataContext)
if (translatedData === null) {
throw new Error('useTranslatedData hook must be used within TranslatedDataProvider')
}
return translatedData
}I get the same error :/
nope,
import type { getTranslatedData } from "@/data/resume"Da_v_idOP
pardon
remove this line, don't try to import functions into client component
Da_v_idOP
'use client'
import React from "react"
type TranslatedData = Awaited<ReturnType< any>>
const TranslatedDataContext = React.createContext<TranslatedData | null>(null)
export default function TranslatedDataProvider({
translatedData,
children,
}: {
translatedData: TranslatedData
children: React.ReactNode
}) {
return (
<TranslatedDataContext.Provider value={translatedData}>
{children}
</TranslatedDataContext.Provider>
)
}
export function useTranslatedData() {
const translatedData = React.useContext(TranslatedDataContext)
if (translatedData === null) {
throw new Error('useTranslatedData hook must be used within TranslatedDataProvider')
}
return translatedData
}"use client"
import React from "react"
const TranslatedDataContext = React.createContext<any | null>(null)
export default function TranslatedDataProvider({
translatedData,
children,
}: {
translatedData: any
children: React.ReactNode
}) {
return (
<TranslatedDataContext.Provider value={translatedData}>
{children}
</TranslatedDataContext.Provider>
)
}
export function useTranslatedData() {
const translatedData = React.useContext(TranslatedDataContext)
if (translatedData === null) {
throw new Error('useTranslatedData hook must be used within TranslatedDataProvider')
}
return translatedData
}Da_v_idOP
should be Awaited?
nope, it's a client component, sync function
show me the file as a screenshot?
Da_v_idOP
export async function generateStaticParams() {
return [{ lang: 'en-US' }, { lang: 'it' }]
}
export default async function RootLayout({
children,
params: { locale },
}: Readonly<{
children: React.ReactNode;
params: {
locale: Locale;
};
}>) {
const translatedData = await getTranslatedData(locale);
return (
<html lang={locale} suppressHydrationWarning>
<body
className={cn(
"min-h-screen bg-background font-sans antialiased max-w-2xl mx-auto py-12 sm:py-24 px-6",
fontSans.variable
)}
>
<TranslatedDataProvider translatedData={translatedData}>
<ThemeProvider attribute="class" defaultTheme="light">
<TooltipProvider delayDuration={0}>
{children}
<Navbar />
</TooltipProvider>
</ThemeProvider>
</TranslatedDataProvider>
</body>
</html>
);
}which file?
TranslatedDataProvider.tsx
Da_v_idOP
do we get the same error if we comment out <TranslatedDataProvider> layer in the layout.tsx?
export async function generateStaticParams() {
return [{ lang: 'en-US' }, { lang: 'it' }]
}
export default async function RootLayout({
children,
params: { locale },
}: Readonly<{
children: React.ReactNode;
params: {
locale: Locale;
};
}>) {
//const translatedData = await getTranslatedData(locale);
return (
<html lang={locale} suppressHydrationWarning>
<body
className={cn(
"min-h-screen bg-background font-sans antialiased max-w-2xl mx-auto py-12 sm:py-24 px-6",
fontSans.variable
)}
>
<ThemeProvider attribute="class" defaultTheme="light">
<TooltipProvider delayDuration={0}>
{children}
<Navbar />
</TooltipProvider>
</ThemeProvider>
</body>
</html>
);
}Da_v_idOP
no it works!
but i can't access the data in client components from the context :/
yeah, ofc just wanted to check if it's really due to that provider
Da_v_idOP
oh then I guess it is
can we have the provider component and hook in different files? not sure if it's the reason but looks off
remove await? but it's async function I remember from your code
then it means it's not async function indeed
so make all of your util functions sync
and see if it still works
@Da_v_id I splitted the hook and provider:
Okay I believe that's a good practice
@James4u try to make `getDicionary` sync function to see if it still works
Da_v_idOP
I copied getDictionary from vercel tutorialhttps://github.com/vercel/next.js/blob/canary/examples/app-dir-i18n-routing/get-dictionary.ts
Da_v_idOP
you mean i should remove async?
yeah, try that
i think i have the same issue of this person: https://github.com/vercel/next.js/discussions/57405#discussioncomment-9304888
Da_v_idOP
Done, I am so dumb, I had to stringify the data before passing them to the provider...
I am so sorry for wasting your time :/
no worries, just mark your solution
so that others can easily know what's that