Getting TypeError, cant read `req` from `initPageResult` when developing plugin
Closed this issue · 14 comments
Describe the Bug
I'm upgrading my plugin from a v2 plugin to a v3 plugin.
The following code gives me TypeError: Cannot read properties of undefined (reading 'req')
const AppointmentsList: React.FC<AdminViewProps> = ({
initPageResult,
params,
searchParams,
}) => {
// rest of code
return (
<DefaultTemplate
i18n={initPageResult.req.i18n}
locale={initPageResult.locale}
params={params}
payload={initPageResult.req.payload}
permissions={initPageResult.permissions}
searchParams={searchParams}
user={initPageResult.req.user || undefined}
visibleEntities={initPageResult.visibleEntities}
>
<div className="collection-list appointments-calendar-view">
<h1>Appointments List</h1>
// rest of code
</div>
</DefaultTemplate>
);
};
Link to the code that reproduces this issue
https://github.com/ahmetskilinc/payload-appointments-plugin/tree/upgrade-to-3.0
Reproduction Steps
- clone repo
$ cd /payload-appointments-plugin/dev
$ pnpm dev
- navigate to
localhost:3000
- should see error.
Which area(s) are affected? (Select all that apply)
plugin: other
Environment Info
React: 19.0.0-rc-3edc000d-20240926
Node: 20.12.0
pnpm: 9.12.1
payload: 3.0.0-beta.120
Same in .122
Looking at your code, you've got 'use client'
at the very top. This component has to be a server component if you want access to req and the local API. If you need client side behaviour, you can include a client side component to handle it and pass it the props that you need.
Remove the use client but know that react hooks wont work in this component either, you'll need to move those down to a client component.
Also as of beta 121, custom views are public by default so remember to add your access control on the req.user
.
Let me know if this fixes your issues here
@paulpopus thanks so much This worked. Added a client component for the relevant parts and passed in as props.
What do you mean by custom views are public? Oh you mean they are public without needing authentication/authorisation and need custom access control fort them?
you mean they are public without needing authentication/authorisation and need custom access control fort them?
Yep! To access control them you can just check the user
on the req
on the server side and then call notFound
or a redirect
depending on what you wanna do
closing this issue then!
I'm still getting initPageResults as undefined, I'm trying to create a custom list view Component. I don't have use client or anything, and using the AdminViewProps. Any idea what the issue is here? on the latest beta.
import React from 'react'
import { Gutter } from '@payloadcms/ui'
import { AdminViewProps } from 'payload'
export default async function MediaList(props: AdminViewProps) {
const { initPageResult, params, searchParams } = props
console.log('page result: ', initPageResult)
return (
<Gutter>
<pre>{JSON.stringify(initPageResult, null, 2)}</pre>
</Gutter>
)
}
@ForrestDevs check my example - https://github.com/ahmetskilinc/payload-appointments-plugin/tree/upgrade-to-3.0/src/views/AppointmentsList
May help you?
How are you using it in your plugin?
Only difference is that I'm defining a custom list view Component, this is how i defined it in my Media Collection;
admin: {
components: {
views: {
list: {
Component: 'src/components/payload/AdminViews/Media/List',
},
},
},
},
@ForrestDevs try to log what props are without a type and see what you get..
aha, well thats interesting, there's a whole load of things when a console.log(props), it appears that initPageResults is already destructured in the prop object.
@ForrestDevs My guess would be that in a List component has different prop type to an Admin view.
It appears that the payload src code still uses that same type for the props in the default ListView, not sure how this is working...
https://github.com/payloadcms/payload/blob/beta/packages/next/src/views/List/index.tsx
This is the result I get after directly logging the props:
note it has a similar structure to the AdminViewProps however it seems things like initPageResult have already been destructured.
cleaned up the output for brevity sake.
{
collectionConfig: {
access: {
create: [Function: authenticated],
delete: [Function: authenticated],
read: [Function: anyone],
unlock: [Function: __TURBOPACK__default__export__],
update: [Function: authenticated]
},
admin: {
components: [Object],
custom: {},
enableRichTextLink: true,
enableRichTextRelationship: true,
pagination: [Object],
useAsTitle: 'filename'
},
auth: false,
custom: {},
endpoints: [],
fields: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object]
],
hooks: {
afterChange: [],
afterDelete: [Array],
afterForgotPassword: [],
afterLogin: [],
afterLogout: [],
afterMe: [],
afterOperation: [],
afterRead: [],
afterRefresh: [],
beforeChange: [Array],
beforeDelete: [],
beforeLogin: [],
beforeOperation: [],
beforeRead: [],
beforeValidate: [],
me: [],
refresh: []
},
timestamps: true,
upload: {
imageSizes: [Array],
focalPoint: false,
crop: false,
disableLocalStorage: true,
adapter: 's3',
handlers: [Array],
bulkUpload: true,
staticDir: 'media'
},
versions: false,
slug: 'media',
labels: { plural: 'Media', singular: 'Media' },
joins: {}
},
collectionSlug: 'media',
data: {
docs: [],
totalDocs: 43,
limit: 25,
totalPages: 2,
page: 1,
pagingCounter: 1,
hasPrevPage: false,
hasNextPage: true,
prevPage: null,
nextPage: 2
},
hasCreatePermission: true,
i18n: {},
limit: 25,
listPreferences: { sort: null, limit: 25 },
listSearchableFields: undefined,
locale: undefined,
newDocumentURL: '/admin/collections/media/create',
params: { segments: [ 'collections', 'media' ] },
payload: <ref *1> BasePayload {
auth: [AsyncFunction: auth],
authStrategies: [ [Object] ],
collections: {},
config: {},
count: [AsyncFunction: count],
create: [AsyncFunction: create],
db: {},
decrypt: [Function: decrypt],
duplicate: [AsyncFunction: duplicate],
email: {},
encrypt: [Function: encrypt],
extensions: undefined,
find: [AsyncFunction: find],
findByID: [AsyncFunction: findByID],
findGlobal: [AsyncFunction: findGlobal],
findGlobalVersionByID: [AsyncFunction: findGlobalVersionByID],
findGlobalVersions: [AsyncFunction: findGlobalVersions],
findVersionByID: [AsyncFunction: findVersionByID],
findVersions: [AsyncFunction: findVersions],
forgotPassword: [AsyncFunction: forgotPassword],
getAdminURL: [Function: getAdminURL],
getAPIURL: [Function: getAPIURL],
globals: { config: [Array] },
importMap: {},
jobs: { queue: [AsyncFunction: queue], run: [AsyncFunction: run] },
logger: {},
login: [AsyncFunction: login],
resetPassword: [AsyncFunction: resetPassword],
restoreGlobalVersion: [AsyncFunction: restoreGlobalVersion],
restoreVersion: [AsyncFunction: restoreVersion],
schema: undefined,
secret: '',
sendEmail: [AsyncFunction: sendEmail],
types: undefined,
unlock: [AsyncFunction: unlock],
updateGlobal: [AsyncFunction: updateGlobal],
validationRules: undefined,
verifyEmail: [AsyncFunction: verifyEmail],
versions: {}
},
permissions: {
canAccessAdmin: true,
collections: {},
globals: {},
},
searchParams: { limit: '25' },
user: {}
}
Update: after reviewing the default list view more closely I realized that because I was defining a Component rather than a Whole View that the props being passed were from the createMappedComponent
function below.
https://github.com/payloadcms/payload/blob/beta/packages/next/src/views/List/index.tsx
const createMappedComponent = getCreateMappedComponent({
importMap: payload.importMap,
serverProps: {
collectionConfig,
collectionSlug,
data,
hasCreatePermission: permissions?.collections?.[collectionSlug]?.create?.permission,
i18n,
limit,
listPreferences,
listSearchableFields: collectionConfig.admin.listSearchableFields,
locale: fullLocale,
newDocumentURL: formatAdminURL({
adminRoute,
path: `/collections/${collectionSlug}/create`,
}),
params,
payload,
permissions,
searchParams,
user,
},
})
@ahmetskilinc Thanks for your help!
This issue has been automatically locked.
Please open a new issue if this issue persists with any additional detail.