Only plain objects can be passed to Client Components from Server Components. Decimal objects not
Answered
Amorph posted this in #help-forum
AmorphOP
104 Replies
AmorphOP
I have a products form where i can edit or add a new product.
When I want to go a single product page to edit it, i get this warning in console:
When I want to go a single product page to edit it, i get this warning in console:
Warning: Only plain objects can be passed to Client Components from Server Components. Decimal objects are not supported.
{id: ..., storeId: ..., categoryId: ..., name: ..., price: Decimal, isFeatured: ..., isArchived: ..., sizeId: ..., colorId: ..., createdAt: ..., updatedAt: ..., images: ...}
Product page is:
import ProductsForm from "@/components/products-form";
import {
getCategories,
getColors,
getProductWithOnlyImagesRelation,
getSizes,
} from "@/server/data-access-layer";
export default async function ProductPage({
params,
}: {
params: { productId: string; storeId: string };
}) {
const product = await getProductWithOnlyImagesRelation(params.productId);
const formattedProduct = {
...product,
price: product?.price.toNumber(),
};
const categories = await getCategories(params.storeId);
const sizes = await getSizes(params.storeId);
const colors = await getColors(params.storeId);
return (
<div className="flex-col">
<div className="flex-1 space-y-4 p-8 pt-6">
<ProductsForm
categories={categories}
colors={colors}
sizes={sizes}
initialData={formattedProduct}
/>
</div>
</div>
);
}
Is there a better way than formatting the product and attaching it in the initialData prop instead of the normal product?
If I use formattedProduct instead, I get this from initial data prop:
Type '{ price: number | undefined; images?: { id: string; productId: string; url: string; createdAt: Date; updatedAt: Date; }[] | undefined; id?: string | undefined; storeId?: string | undefined; ... 7 more ...; updatedAt?: Date | undefined; }' is not assignable to type '{ id: string; storeId: string; categoryId: string; name: string; price: Decimal; isFeatured: boolean; isArchived: boolean; sizeId: string; colorId: string; createdAt: Date; updatedAt: Date; } & { ...; }'.
Type '{ price: number | undefined; images?: { id: string; productId: string; url: string; createdAt: Date; updatedAt: Date; }[] | undefined; id?: string | undefined; storeId?: string | undefined; ... 7 more ...; updatedAt?: Date | undefined; }' is not assignable to type '{ id: string; storeId: string; categoryId: string; name: string; price: Decimal; isFeatured: boolean; isArchived: boolean; sizeId: string; colorId: string; createdAt: Date; updatedAt: Date; }'.
Types of property 'id' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2322)
types.ts(169, 3): The expected type comes from property 'initialData' which is declared here on type 'IntrinsicAttributes & ProductFormProps'
AmorphOP
bump
AmorphOP
bump
AmorphOP
bump
AmorphOP
bump
AmorphOP
bump
AmorphOP
bump
wow props for following the rules ngl
cant you just use Numbers instead of.. Decimal?
Or if you need to use Decimal, the issue is that Decimals cant be serialized in js atm
so pass it as a string to the client component
decimalValue.toString()
then you can convert back like this
const decimal = new Decimal(decimalAsString);
@Amorph
AmorphOP
yes i know, hence why i formatted my product to number
import ProductsForm from "@/components/products-form";
import {
getCategories,
getColors,
getProductWithOnlyImagesRelation,
getSizes,
} from "@/server/data-access-layer";
export default async function ProductPage({
params,
}: {
params: { productId: string; storeId: string };
}) {
const product = await getProductWithOnlyImagesRelation(params.productId);
const formattedProduct = {
...product,
price: product?.price.toNumber(),
};
const categories = await getCategories(params.storeId);
const sizes = await getSizes(params.storeId);
const colors = await getColors(params.storeId);
return (
<div className="flex-col">
<div className="flex-1 space-y-4 p-8 pt-6">
<ProductsForm
categories={categories}
colors={colors}
sizes={sizes}
initialData={formattedProduct}
/>
</div>
</div>
);
}
oh
AmorphOP
to number or can also do string as u said since those can be passed to client components
but
Warning: Only plain objects can be passed to Client Components from Server Components. Decimal objects are not supported.
You dont see this anymore then right?
You dont see this anymore then right?
AmorphOP
yes i have problem with the types now
if i hover over initialData prop
Type '{ price: number | undefined; images?: { id: string; productId: string; url: string; createdAt: Date; updatedAt: Date; }[] | undefined; id?: string | undefined; storeId?: string | undefined; ... 7 more ...; updatedAt?: Date | undefined; }' is not assignable to type '{ id: string; storeId: string; categoryId: string; name: string; price: Decimal; isFeatured: boolean; isArchived: boolean; sizeId: string; colorId: string; createdAt: Date; updatedAt: Date; } & { ...; }'.
Type '{ price: number | undefined; images?: { id: string; productId: string; url: string; createdAt: Date; updatedAt: Date; }[] | undefined; id?: string | undefined; storeId?: string | undefined; ... 7 more ...; updatedAt?: Date | undefined; }' is not assignable to type '{ id: string; storeId: string; categoryId: string; name: string; price: Decimal; isFeatured: boolean; isArchived: boolean; sizeId: string; colorId: string; createdAt: Date; updatedAt: Date; }'.
Types of property 'id' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2322)
types.ts(169, 3): The expected type comes from property 'initialData' which is declared here on type 'IntrinsicAttributes & ProductFormProps'
Type '{ price: number | undefined; images?: { id: string; productId: string; url: string; createdAt: Date; updatedAt: Date; }[] | undefined; id?: string | undefined; storeId?: string | undefined; ... 7 more ...; updatedAt?: Date | undefined; }' is not assignable to type '{ id: string; storeId: string; categoryId: string; name: string; price: Decimal; isFeatured: boolean; isArchived: boolean; sizeId: string; colorId: string; createdAt: Date; updatedAt: Date; }'.
Types of property 'id' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2322)
types.ts(169, 3): The expected type comes from property 'initialData' which is declared here on type 'IntrinsicAttributes & ProductFormProps'
and if i hover over initial data, it will show like this:
show me <ProductsForm>
AmorphOP
(property) initialData: ({
id: string;
storeId: string;
categoryId: string;
name: string;
price: Decimal;
isFeatured: boolean;
isArchived: boolean;
sizeId: string;
colorId: string;
createdAt: Date;
updatedAt: Date;
} & {
...;
}) | null
id: string;
storeId: string;
categoryId: string;
name: string;
price: Decimal;
isFeatured: boolean;
isArchived: boolean;
sizeId: string;
colorId: string;
createdAt: Date;
updatedAt: Date;
} & {
...;
}) | null
this is Products form
types are:
export type ProductFormProps = {
initialData: (Product & { images: Image[] }) | null;
categories: Category[];
colors: Color[];
sizes: Size[];
};
and my product model
model Product {
id String @id @default (uuid())
storeId String
store Store @relation("StoreToProduct", fields: [storeId], references: [id])
categoryId String
category Category @relation("CategoryToProduct", fields: [categoryId], references: [id])
name String
price Decimal
isFeatured Boolean @default(false)
isArchived Boolean @default(false)
sizeId String
size Size @relation(fields: [sizeId],references: [id])
colorId String
color Color @relation(fields: [colorId], references: [id])
images Image[]
orderItems OrderItem[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([storeId])
@@index([categoryId])
@@index([sizeId])
@@index([colorId])
}
chill
lol
AmorphOP
in case ur next questions would be how props look lol
its an easy fix, so basically
const product = await getProductWithOnlyImagesRelation(params.productId);
hover over product
and tell me what you see for the type of id
AmorphOP
string
or u mean type of price?
just string or string | undefined
AmorphOP
just string
const product: ({
images: {
id: string;
productId: string;
url: string;
createdAt: Date;
updatedAt: Date;
}[];
} & {
id: string;
storeId: string;
categoryId: string;
name: string;
price: Decimal;
isFeatured: boolean;
isArchived: boolean;
sizeId: string;
colorId: string;
createdAt: Date;
updatedAt: Date;
}) | null
images: {
id: string;
productId: string;
url: string;
createdAt: Date;
updatedAt: Date;
}[];
} & {
id: string;
storeId: string;
categoryId: string;
name: string;
price: Decimal;
isFeatured: boolean;
isArchived: boolean;
sizeId: string;
colorId: string;
createdAt: Date;
updatedAt: Date;
}) | null
thats how it looks
createdAt: Date; updatedAt: Date; }'.
Types of property 'id' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2322)
Types of property 'id' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2322)
one of your ids are undefined
real quick, paste this
const formattedProduct = {
...product,
id: product?.id ?? "",
price: product?.price.toNumber(),
};
AmorphOP
now it continues to the next one
Types of property 'storeId' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2322)
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2322)
AmorphOP
yes
now null isnt an object, it dosent have properties. hence why null.id is undefined
you need to add a like checker for it.
you need to add a like checker for it.
AmorphOP
if there is initial data then it will use initial data
but it can be null
mhm
AmorphOP
so basically ur saying to format all the fields
yes.
AmorphOP
like i have price/id, but do for all
yes.
id: product?.id ?? "",
Answer
if the id exists, its gonna use it.. if its undefined.. it will use ""
AmorphOP
ok, let me do this and get back to u
sure
this is why we love typescript ❤️
AmorphOP
@Arinji ok now i got this
Types of property 'price' are incompatible.
Type 'number' is not assignable to type 'Decimal'.ts(2322)
Type 'number' is not assignable to type 'Decimal'.ts(2322)
const formattedProduct = {
...product,
id: product?.id ?? "",
storeId: product?.storeId ?? "",
categoryId: product?.categoryId ?? "",
name: product?.name ?? "",
price: product?.price.toNumber() ?? 0,
};
...product,
id: product?.id ?? "",
storeId: product?.storeId ?? "",
categoryId: product?.categoryId ?? "",
name: product?.name ?? "",
price: product?.price.toNumber() ?? 0,
};
AmorphOP
if im understanding right, im passing a number, but my product form props are using the ProductFormProps types
and initial data type is made of Product model which has price: decimal
so im not sure to go around this, since i want to keep decimal in my model
make initial data accept a Number
then convert it back to Decimal
once you are in the client component
AmorphOP
initialData: (Product & { images: Image[] }) | null;
@Arinji u mean this one right?
and instead of "Product" ill just write the fields myself
and have the price be number not decimal
yea
AmorphOP
@Arinji when it comes to type Date
what would the empty value be here?
as in, for strings its ""
but what is it for a Date type?
AmorphOP
new Date(0) to set it to unix epoch or new Date() ?
AmorphOP
@Arinji like this? createdAt: product?.createdAt.getTime().toString() ?? "",
because they will still be incompatible
AmorphOP
actually found much easier way:D instead of accounting for all the variables, i just conditionally format my product only if i already have some data in it
const formattedProduct = product
? {
...product,
price: product?.price.toNumber() ?? 0,
}
: null;
thanks for all ur help nonetheless
Sure, make your types as needed