Reputation: 277
I have created an app to be able to search care homes on a map and add new ones through a form.
Background: Everything worked in dev mode but in production the map was not refreshing to include the newly added care homes.The api to fetch the care home data was running at build time and after that subsequent requests return the same data ie don't include new data. (I don't understand why that was happening)
As a solution I used a server action to get the carehome data and return it to the client map component.
Now I am getting an warning saying that Only plain objects can be passed to Client Components from Server Components. Objects with toJSON methods are not supported. Convert it manually to a simple value before passing it to props.
If you have any ideas why an api would only run at build time or how I can solve this warning I would really appreciate it. I am relatively beginner with next.js and development in general so apologies if this is not clear.
link to repo: https://github.com/niallam22/lottie/blob/main/src/app/components/careHomeMap.jsx
_actions.js
export async function getCareHomes(){
try {
await connectMongoDB();
const careHomes = await CareHome.find();
console.log('api/carehome careHomes: ',careHomes);
// Return the list of care homes in the response
return careHomes;
} catch (error) {
// Handle any errors that occur during the query
console.error('Error:', error);
// Return an error response
return { message: 'Error fetching care homes' };
}
}
'use client'
import { useEffect, useState } from "react";
import { getCareHomes } from '../_actions'
export default function CareHomeMap({ map, home }) {
const [careHomeData, setCareHomeData] = useState();
useEffect(()=>{
try {
getCareHomes().then(data =>{
setCareHomeData(data)
})
// fetchCareHomes().then(data =>{
// setCareHomeData(data)
// })
} catch (error) {
console.log(error)
}
},[])
Upvotes: 26
Views: 98051
Reputation: 1926
I had this issue due to passing down result of getServerSession()
to client component. This happens when there is an error in the method. success result works fine.
I simply created a server side reusable method which handles error and returns required data instead of entire object like session.user or null.
try{
const session = await getServerSession();
return {user:session.user}
}catch(...){
return {user:null, } // or {}
}
Upvotes: 0
Reputation: 1
The error occurs because mongoose documents contain methods and complex objects that can't be directly serialized when passing from server to client components. we need to convert the mongoose document to a plain javascript object and ensure the _id is properly stringified.
Here's the solution that works in my case
"use server";
import { Connect } from "@/db/connection";
import { UserModel } from "@/models";
export const registerUser = async (user) => {
await Connect();
const newUser = await UserModel.create(user);
const savedUser = await newUser.save();
const userData = {
...savedUser.toObject(),
_id: savedUser._id.toString(),
};
return {
message: "User registered successfully",
data: userData,
};
};
Upvotes: 0
Reputation: 878
I realise I'm late to the party. But, in case anyone comes looking who's using a standard fetch
and getting this error, I fixed this by adding .json()
to the response.
Got the above error when using:
{
...
const response = await fetch(url, options);
return response as T;
}
Fixed it using:
...
const response = await fetch(url, options).then((res) =>{
return res.json();
});
return response as T;
}
And this is where T
is a variety of incredibly complex objects.
Upvotes: 0
Reputation: 21
This happens when you pass an object with methods from a server to a client component. The values of the object should not contain methods/functions.
Look at this example with iron-session
:
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const session = await getSession();
const {
destroy: _destroy,
save: _save,
updateConfig: _updateConfig,
...rest
} = session;
return (
<html>
<body className={inter.className}>
<AuthProvider session={rest}>
<Navbar />
<main>{children}</main>
<Toaster />
</AuthProvider>
</body>
</html>
);
}
Here I'm removing the methods inside the session
object usually used to control the session.
It's just you need to purge the passed object from methods in one way or another...
Upvotes: 1
Reputation: 74
Not sure if this question is still relevant to somebody but I have a short helper function to cop with this issue
export async function sanitizeData(data:any) {
return JSON.parse(JSON.stringify(data));
}
and using it as follow
studentDetails = await sanitizeData(studentDetails);
I initially had this issue while using prisma raw query at the backend and on frontend I have to sanatice the data
Upvotes: 0
Reputation: 1
This worked for me: The Query.prototype.lean() function returns the documents from queries with the lean option enabled as plain JavaScript objects, not Mongoose Documents read more about .lean().
And try this out in your code:
const careHomes = await CareHome.find().lean()
Upvotes: 0
Reputation: 489
you should before passing data to page for use data, convert to string and so to json
const data = JSON.parse(JSON.stringify(sampleData))
Upvotes: 48
Reputation: 319
If you're running into this while using React Query + Server Actions.
Instead of this:
const { data: subscriptionInfo } = useQuery(QUERY_KEYS.subscription,
fetchSubscriptionInfo
);
You may wanna do:
const { data: subscriptionInfo } = useQuery(QUERY_KEYS.subscription, () => {
return fetchSubscriptionInfo();
});
fetchSubscriptionInfo is a Server Action.
The reason is similar to the event handling ones above. Although I believe the first one will still work despite the error, since fetchSubscriptionInfo does not take in any params. Basically React Query is trying to pass a complex param(not form/object) to the Server Action, and React lets you know that it can't parse it.
Upvotes: 2
Reputation: 1
I had a similar issue when and it brought me to this article, which also gave me a solution.
In your case, you are passing a server component to a client component, which can only accept plain objects. In your case, careHomes that you are getting from mongoDB is not a plain object. In my case, I'm using sanity.
The client component can not read API data due to its format.
In my case, I wrote my API route as you did but I have an action that calls the data into the client component but does not return the data as a response.json()
.
So the solution to this problem is response.json()
Upvotes: 0
Reputation: 810
Server Actions:
export const deleteCompleted = async (): Promise<void> => {
await prisma.todo.deleteMany({
where: { complete: true },
});
revalidatePath("/dashboard/server-todos");
};
Frontend:
<button
onClick={() => {
deleteCompleted();
}}
type="button"
className="flex items-center justify-center rounded ml-2 bg-red-400 p-2 text-white hover:bg-red-700 transition-all"
>
<IoTrashOutline />
Borrar completados
</button>
Upvotes: 0
Reputation: 1377
I encountered this error because I was passing the reference of a server function to an onClick
handler. Passing the reference of the function to the handler makes sure the event handler parameters are passed to the server function and that doesn't seem to be allowed.
onClick={revalidateEvents}
throws the error because this essentially equals:
onClick={(event) => {revalidateEvents(event)}}
So the solution for me was
onClick={() => {revalidateEvents()}}
Upvotes: 12
Reputation: 81
For me, I encountered this same error because I had mistakenly named a file under NextJs api folder as page.ts
instead of route.ts
Upvotes: 1
Reputation: 6829
I'm assuming that your server action returns ("careHomes") an unsupported type.
The arguments and return value of Server Actions must be "React serializable values": https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#behavior
The supported types for Server Action arguments:
Primitives
Iterables containing serializable values
String
Array
Map
Set
TypedArray and ArrayBuffer
Date
FormData instances
Plain objects: those created with object initializers, with serializable properties
Functions that are Server Actions
Promises
Notably, these are not supported:
Source: https://react.dev/reference/react/use-server#serializable-parameters-and-return-values
Upvotes: 2
Reputation: 41
I had the same issue and it was caused by _id
from MongoDB not being a plain object, I have used toString()
and it works.
Upvotes: 0
Reputation: 192
This is because you are passing a server component to a client component and client component can only accept plain objects.
In your case, careHomes
that you are getting from mongoDB is not a plain object. Documents coming from MongoDB has this ._id
which is a reference to a complex object. If you print out your return from mongoDB you'll see something like this(_id
):
_id: new ObjectId('65aed9609c564bf65becf14a'),
title: 'User Interface Enhancement',
description: ' Improve the user interface of the application to enhance user experience and modernize the overall look and feel.',
category: 'Projects',
priority: 4,
progress: 28,
I can recommend you 2 solutions:
.toString()
careHomes._id = careHomes._id.toString()
._id
`${careHomes._id}`
Upvotes: 4