payloadcms / payload

The best way to build a modern backend + admin UI. No black magic, all TypeScript, and fully open-source, Payload is both an app framework and a headless CMS.
https://payloadcms.com
MIT License
21.65k stars 1.3k forks source link

[3.0.0-beta.30] Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components #6351

Open mixty1 opened 2 months ago

mixty1 commented 2 months ago

Link to reproduction

No response

Describe the Bug

update version from 3.0.0-beta.24 to 3.0.0-beta.30 and get some errors

key not found:  upload:Sizes
 ⨯ Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
    at stringify (<anonymous>)
    at stringify (<anonymous>)
digest: "1852293969"
 ⨯ Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
    at stringify (<anonymous>)
    at stringify (<anonymous>)
    at stringify (<anonymous>)
    at stringify (<anonymous>)
digest: "1371415116"
 ⨯ Internal error: TypeError: Cannot read properties of null (reading 'digest')
    at Object.onError (/Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:36:17945)
    at al (/Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:9931)
    at /Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:59036
    at /Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:59252
    at aA (/Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:59260)
    at Timeout._onTimeout (/Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:6971)
    at listOnTimeout (node:internal/timers:573:17)
    at process.processTimers (node:internal/timers:514:7)
digest: "2263670172"
 GET /admin 500 in 9698ms
 ⨯ Error: Attempted to call useWatchForm() from the server but useWatchForm is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.
    at /Users/{{projectFolder}}/.next/server/chunks/ssr/3bbea_@payloadcms_ui_dist_e5e06c._.js:1181:11
    at LinkToDoc (/Users/{{projectFolder}}/.next/server/chunks/ssr/node_modules__pnpm_35a647._.js:13319:414)
    at stringify (<anonymous>)
digest: "323871085"
 ✓ Compiled in 1669ms
 ✓ Compiled /admin/[[...segments]] in 22ms
key not found:  upload:Sizes
 ⨯ Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
    at stringify (<anonymous>)
    at stringify (<anonymous>)
digest: "1852293969"
 ⨯ Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
    at stringify (<anonymous>)
    at stringify (<anonymous>)
    at stringify (<anonymous>)
    at stringify (<anonymous>)
digest: "1371415116"
 ⨯ Error: Attempted to call useWatchForm() from the server but useWatchForm is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.
    at /Users/{{projectFolder}}/.next/server/chunks/ssr/3bbea_@payloadcms_ui_dist_e5e06c._.js:1181:11
    at LinkToDoc (/Users/{{projectFolder}}/.next/server/chunks/ssr/node_modules__pnpm_35a647._.js:13319:414)
    at stringify (<anonymous>)
digest: "323871085"
 ⨯ Internal error: TypeError: Cannot read properties of null (reading 'digest')
    at Object.onError (/Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:36:17945)
    at al (/Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:9931)
    at /Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:59036
    at /Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:59252
    at aA (/Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:59260)
    at Timeout._onTimeout (/Users/{{projectFolder}}/node_modules/.pnpm/next@14.3.0-canary.28_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.76.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:6971)
    at listOnTimeout (node:internal/timers:573:17)
    at process.processTimers (node:internal/timers:514:7)
digest: "2263670172"
 GET /admin 500 in 204ms

I have many collections and this part of error – key not found: upload:Sizes comes from my Media collection for uploading images, and it's have field imageSizes, if i remove this field, key not found: upload:Sizes is gone, but i need this field. I don't know yet how to fix other errors, because before this version all was ok. And unfortunately i can't reproduce all structure of my app, but i have typical structure for e-commerce.

To Reproduce

install payload@3.0.0-beta.30 @payloadcms/db-postgres@3.0.0-beta.30 @payloadcms/next@3.0.0-beta.30 @payloadcms/plugin-nested-docs@3.0.0-beta.30 @payloadcms/plugin-search@3.0.0-beta.30 @payloadcms/richtext-slate@3.0.0-beta.30 @payloadcms/ui@3.0.0-beta.30 @payloadcms/translations@3.0.0-beta.30 and load admin main page

Payload Version

3.0.0-beta.30

Adapters and Plugins

db-postgres, plugin-nested-docs, plugin-search, richtext-slate

jmikrut commented 2 months ago

Hey - we will need a minimal reproduction in order to help you here. Can we see your config? And your collection configs where upload is enabled?

mixty1 commented 2 months ago

Hey! Thx for quick response, ok i can show config and some collections code.

// payload.config.ts

import path from 'path'
import sharp from 'sharp'
import { fileURLToPath } from 'url'
import { en } from 'payload/i18n/en'
import { ru } from 'payload/i18n/ru'
import { buildConfig } from 'payload/config'
import { slateEditor } from '@payloadcms/richtext-slate'
import { postgresAdapter } from '@payloadcms/db-postgres'
import { nestedDocsPlugin } from '@payloadcms/plugin-nested-docs'
import { searchPlugin } from '@payloadcms/plugin-search'

import Users from './src/collections/Users'
import Products from './src/collections/Products'
import Categories from './src/collections/Categories'
import Tags from './src/collections/Tags'
import Media from './src/collections/Media'
import SpecGroup from './src/collections/SpecGroup'
import SpecName from './src/collections/SpecName'
import SpecValue from './src/collections/SpecValue'
import ProductSpecs from './src/collections/ProductSpecs'
import Pages from './src/collections/Pages'
import Widgets from './src/collections/Widgets'
import Orders from './src/collections/Orders'

const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)

export default buildConfig({
  cors: process.env.NODE_ENV === 'production' ? undefined : '*',
  admin: {
    user: Users.slug,
    // css: path.resolve(__dirname, './src/assets/styles/base.scss'),
    // bundler: viteBundler(),
  },
  editor: slateEditor({}),
  collections: [
    Users,
    Products,
    Categories,
    Tags,
    Media,
    ProductSpecs,
    SpecGroup,
    SpecName,
    SpecValue,
    Pages,
    Widgets,
    Orders,
  ],
  secret: process.env.PAYLOAD_SECRET || '',
  typescript: {
    outputFile: path.resolve(dirname, 'payload-types.ts'),
  },
  plugins: [
    // payloadCloud(),
    searchPlugin({
      collections: [Products.slug],
      searchOverrides: {
        admin: {
          hidden: true,
        },
      },
    }),
    nestedDocsPlugin({
      collections: [Categories.slug],
      generateURL: (docs) => docs.reduce((_, doc) => `/catalog/${doc.slug}`, ''),
    }),
  ],
  db: postgresAdapter({
    pool: {
      connectionString: process.env.DATABASE_URI,
    },
  }),
  i18n: {
    supportedLanguages: { en, ru },
  },
  sharp,
})
// src/collections/Media.ts
import { CollectionConfig } from 'payload/types'

const Media: CollectionConfig = {
  slug: 'media',
  access: {
    read: () => true,
    create: () => true,
  },
  labels: {
    singular: {
      en: 'Media',
      ru: 'Медиа',
    },
    plural: {
      en: 'Media',
      ru: 'Медиа',
    },
  },
  fields: [
    {
      name: 'alt',
      type: 'text',
    },
  ],
  upload: {
    staticDir: 'media',
    imageSizes: [
      {
        name: 'thumbnail',
        width: 256,
        height: 256,
        position: 'centre',
      },
      {
        name: 'card',
        width: 512,
        height: 512,
        position: 'centre',
      },
      {
        name: 'tablet',
        width: 1024,
        height: 1024,
        position: 'centre',
      },
      {
        name: 'banner',
        width: 1256,
        height: 400,
        position: 'centre',
      },
    ],
    adminThumbnail: 'thumbnail',
    mimeTypes: ['image/*'],
  },
}

export default Media
r1tsuu commented 2 months ago

i can't see it from your code, but if you somewhere have something like this with custom component

Field: (props) => <Component {...props} />

And Component is client component, then you'll get this error. Because there was a breaking change, that also was actually a fix with passing payload into custom component. You need to delete payload key before passing it

Field: ({ payload: _payload, ...props }) => <Component {...props} />
mixty1 commented 2 months ago

Yes, i have custom components in my Products collection which has client-only components, but if i comment lines with this components, errors doesn't gone.

I tried like you say:

Field: ({ payload: _payload, ...props }) => <Uppy {...props} />,

Field: ({ payload: _payload, ...props }) => <SpecList {...props} />,

but it's not working.

Products collection:

// src/collections/Products.ts

import { CollectionConfig } from 'payload/types'
import Tags from '../Tags'
import ProductSpecs from '../ProductSpecs'
import SpecList from './ui/SpecList'
import SpecGroup from '../SpecGroup'
import SpecName from '../SpecName'
import SpecValue from '../SpecValue'
import productSpecsHook from './hooks/product-specs'
import productMediaHook from './hooks/product-media'
// import initSpecsHook from './hooks/init-specs'
// import validateSpecs from './validations/validate-specs'
import Uppy from '../../app/(payload)/components/Uppy'
import Media from '../Media'
import SaveButton from './ui/SaveButton'
import generateSlug from '../hooks/generate-slug'

const Products: CollectionConfig = {
  slug: 'products',
  access: {
    read: () => true,
    create: () => true,
    update: () => true,
  },
  hooks: {
    beforeOperation: [generateSlug],
  },
  admin: {
    useAsTitle: 'title',
    components: {
      edit: {
        SaveButton,
      },
    },
  },
  labels: {
    singular: {
      en: 'Product',
      ru: 'Товар',
    },
    plural: {
      en: 'Products',
      ru: 'Товары',
    },
  },
  fields: [
    {
      name: 'title',
      type: 'text',
      required: true,
      label: {
        en: 'Title',
        ru: 'Название',
      },
    },
    {
      name: 'slug',
      type: 'text',
      unique: true,
      admin: {
        hidden: true,
      },
      label: {
        en: 'Slug',
        ru: 'Строка',
      },
    },
    {
      name: 'hero',
      type: 'upload',
      relationTo: 'media',
      required: true,
      label: {
        en: 'Hero',
        ru: 'Изображение',
      },
    },
    {
      name: 'category',
      type: 'relationship',
      required: true,
      relationTo: 'categories',
      hasMany: false,
      admin: {
        position: 'sidebar',
      },
      label: {
        en: 'Catergory',
        ru: 'Категория',
      },
    },
    {
      name: 'sku',
      type: 'text',
      required: true,
      unique: true,
      admin: {
        position: 'sidebar',
      },
      label: {
        en: 'Sku',
        ru: 'Артикул',
      },
    },
    {
      name: 'stock',
      type: 'number',
      defaultValue: 0,
      required: true,
      admin: {
        position: 'sidebar',
      },
      label: {
        en: 'Stock',
        ru: 'Наличие',
      },
    },
    {
      name: 'status',
      type: 'text',
      admin: {
        position: 'sidebar',
      },
      label: {
        en: 'Status',
        ru: 'Статус',
      },
    },
    {
      name: 'prices',
      type: 'group',
      interfaceName: 'Prices',
      label: {
        en: 'Prices',
        ru: 'Цены',
      },
      admin: {
        position: 'sidebar',
      },
      fields: [
        {
          name: 'retail',
          type: 'number',
          required: true,
          label: {
            en: 'Retail',
            ru: 'Розничная цена',
          },
        },
        {
          name: 'purchase',
          type: 'number',
          label: {
            en: 'Purchase',
            ru: 'Оптовая цена',
          },
        },
        {
          name: 'discount',
          type: 'number',
          label: {
            en: 'Discount',
            ru: 'Скидка',
          },
        },
        {
          name: 'currency',
          type: 'text',
          required: true,
          defaultValue: '₸',
          label: {
            en: 'Currency',
            ru: 'Валюта',
          },
        },
      ],
    },
    {
      name: 'description',
      type: 'richText',
      label: {
        en: 'Description',
        ru: 'Описание',
      },
    },
    {
      name: 'media',
      type: 'relationship',
      relationTo: Media.slug,
      hasMany: true,
      label: {
        en: 'Media',
        ru: 'Медиа файлы',
      },
      admin: {
        components: {
          Field: Uppy,
        },
      },
      // hooks: {
      //   beforeChange: [productMediaHook],
      // },
    },
    {
      name: 'productspecs',
      type: 'relationship',
      relationTo: ProductSpecs.slug,
      hasMany: true,
      label: {
        en: 'Specifications',
        ru: 'Характеристики',
      },
      admin: {
        custom: {
          fields: [
            {
              name: 'group',
              type: 'ui',
              custom: {
                collection: SpecGroup.slug,
              },
              label: {
                en: 'Group',
                ru: 'Группа',
              },
            },
            {
              name: 'name',
              type: 'ui',
              custom: {
                collection: SpecName.slug,
              },
              label: {
                en: 'Name',
                ru: 'Название',
              },
            },
            {
              name: 'value',
              type: 'ui',
              custom: {
                collection: SpecValue.slug,
              },
              label: {
                en: 'Value',
                ru: 'Значение',
              },
            },
          ],
        },
        components: {
          Field: SpecList,
        },
      },
      hooks: {
        beforeChange: [productSpecsHook],
        // afterRead: [initSpecsHook]
      },
      // validate: validateSpecs,
    },
    {
      name: 'dimensions',
      type: 'group',
      interfaceName: 'Dimensions',
      admin: {
        className: 'product-dimensions',
      },
      label: {
        en: 'Dimensions (mm)',
        ru: 'Размеры (мм)',
      },
      fields: [
        {
          name: 'width',
          type: 'number',
          admin: {
            width: '24%',
          },
          label: {
            en: 'Width',
            ru: 'Ширина',
          },
        },
        {
          name: 'height',
          type: 'number',
          admin: {
            width: '24%',
          },
          label: {
            en: 'Height',
            ru: 'Высота',
          },
        },
        {
          name: 'length',
          type: 'number',
          admin: {
            width: '24%',
          },
          label: {
            en: 'Length',
            ru: 'Длина',
          },
        },
        {
          name: 'weight',
          type: 'number',
          admin: {
            width: '24%',
          },
          label: {
            en: 'Weight',
            ru: 'Вес',
          },
        },
      ],
    },
    {
      name: 'model',
      type: 'text',
      label: {
        en: 'Model',
        ru: 'Модель',
      },
    },
    {
      name: 'manufacturer',
      type: 'text',
      label: {
        en: 'Manufacturer',
        ru: 'Производитель',
      },
    },
    {
      name: 'country',
      type: 'text',
      label: {
        en: 'Country',
        ru: 'Страна',
      },
    },
    {
      name: 'isVisible',
      defaultValue: true,
      type: 'checkbox',
      label: {
        en: 'Visiblity',
        ru: 'Видимость',
      },
    },
    {
      name: 'tags',
      type: 'relationship',
      hasMany: true,
      relationTo: Tags.slug,
      admin: {
        position: 'sidebar',
      },
      label: {
        en: 'Tags',
        ru: 'Тэги',
      },
    },
    {
      name: 'views',
      type: 'number',
      hidden: true,
      defaultValue: 0,
      label: {
        en: 'Views',
        ru: 'Просмотры',
      },
    },
  ],
}

export default Products
mixty1 commented 2 months ago

A solution has been found. I'm running the dev server using the --turbo option and maybe it's using some strict options because the UI isn't showing up with this errors, but without --turbo it's showing up normally, although I'm getting the following errors in the console:

key not found:  upload:Sizes
 ⨯ node_modules/.pnpm/@payloadcms+plugin-search@3.0.0-beta.30_@types+react@18.3.1_monaco-editor@0.38.0_next@14.3.0-_xof4nppotimeuxnbzv4dvfsslu/node_modules/@payloadcms/plugin-search/dist/Search/ui/index.js (5:30) @ LinkToDoc
 ⨯ TypeError: (0 , _payloadcms_ui_forms_Form__WEBPACK_IMPORTED_MODULE_0__.useWatchForm) is not a function
    at stringify (<anonymous>)
digest: "2857860722"
  3 | import React from 'react';
  4 | export const LinkToDoc = ()=>{
> 5 |     const form = useWatchForm();
    |                              ^
  6 |     const fields = form.fields;
  7 |     const { doc: { value: { relationTo, value: docId } } } = fields;
  8 |     const config = useConfig();
JarrodMFlesch commented 1 month ago

@mixty1 Please add link to a reproduction repository or we will have to close this.

The repository linked should be as minimal as possible. 1 collection, using a custom component that is giving your trouble.

Thanks!