Search code examples
reactjstypescriptpayload-cms

How to remove error message "Type 'FC<{ path: string; }>' is not assignable to type '(CustomComponent & PayloadComponent<FieldClientComponent)"


I'm new to TypeScript and converting my site from WordPress to Payload and I'm getting an error code that I can't find anywhere to fix. The code works but I would like to have the error message go away.

In my block, I get this error message: Type 'FC<{ path: string; }>' is not assignable to type '(CustomComponent & PayloadComponent<FieldClientComponent | FieldServerComponent>) | undefined'. What does this error message mean? Again, the code works but I would like this error to go away.

This error message is at my field:

{
  name: 'linkURL',
  label: 'URL',
  type: 'text',
  admin: {
    components: {
      Field: CustomLinkField, // Use custom React component
    },
  },
},`

This is my react component:

'use client';  // Add this line

import React, { useState, useEffect } from 'react';
import { useField } from '@payloadcms/ui';

const API_KEY = 'xxxxxx'; // Replace with your actual API key

const CustomLinkField: React.FC<{ path: string }> = ({ path }) => {
  const { value, setValue } = useField<string>({ path }); // Get the linkURL value
  const { setValue: setJsonValue } = useField<string>({ path: path.replace('linkURL', 'linkOutput') }); // Get the linkOutput field

const [loading, setLoading] = useState(false);

useEffect(() => {
  const fetchMetadata = async () => {
    if (value && value.startsWith('http')) {
      setLoading(true);
    try {
      const apiUrl = `https://iframe.ly/api/iframely?url=${encodeURIComponent(value)}&api_key=${API_KEY}`;
      const response = await fetch(apiUrl);
      if (!response.ok) throw new Error('Failed to fetch data');
      const json = await response.json();
      setJsonValue(JSON.stringify(json, null, 2)); // Store JSON output
    } catch (error) {
      console.error('Error fetching link metadata:', error);
      setJsonValue('Error fetching data');
    } finally {
      setLoading(false);
    }
  }
};

fetchMetadata();
}, [value]); // Run effect whenever value (linkURL) changes

return (
  <div>
   <input 
    type="text" 
    value={value || ''} 
    onChange={(e) => setValue(e.target.value)} 
    placeholder="Enter URL" 
    style={{ width: '100%', padding: '8px' }}
  />
  {loading && <p>Loading...</p>}
 </div>
 );
};

export default CustomLinkField;

This is my code on my block (ts) file:

import type { Block } from 'payload';
import CustomLinkField from '../iframely';

export const blockLink: Block = {
  slug: 'block-link',
  interfaceName: 'blockLink',
  labels: {
    singular: 'Link',
    plural: 'Links',
  },
  fields: [
    {
      name: 'linkURL',
      label: 'URL',
      type: 'text',
      admin: {
        components: {
          Field: CustomLinkField, // Use custom React component
        },
      },
    },
    {
      name: 'linkOutput',
      label: 'JSON Output',
      type: 'textarea',
      admin: {
        readOnly: true, // Prevent manual edits
      },
    },
  ],
};

Solution

  • If you explore the definition of type Block and it's property fields, it is of type

     fields: Field[];
    

    CustomLinkField is of type Field

    You will see that Field type is declared as follows:

    export type Field = ArrayField | BlocksField | CheckboxField | CodeField | CollapsibleField | DateField | EmailField | GroupField | JoinField | JSONField | NumberField | PointField | RadioField | RelationshipField | RichTextField | RowField | SelectField | TabsField | TextareaField | TextField | UIField | UploadField;
    

    If you dig a little further into these definitions e.g. TextField

    export type TextField = {
        admin?: {
            autoComplete?: string;
            components?: {
                afterInput?: CustomComponent[];
                beforeInput?: CustomComponent[];
                Error?: CustomComponent<TextFieldErrorClientComponent | TextFieldErrorServerComponent>;
                Label?: CustomComponent<TextFieldLabelClientComponent | TextFieldLabelServerComponent>;
            } & Admin['components'];
            placeholder?: Record<string, string> | string;
            rtl?: boolean;
        } & Admin;
        maxLength?: number;
        minLength?: number;
        type: 'text';
    }
    

    And the Admin definition

    type Admin = {
        className?: string;
        components?: {
            Cell?: PayloadComponent<DefaultServerCellComponentProps, DefaultCellComponentProps>;
            Description?: PayloadComponent<FieldDescriptionServerProps, FieldDescriptionClientProps>;
            Diff?: PayloadComponent<FieldDiffServerProps, FieldDiffClientComponent>;
            Field?: PayloadComponent<FieldClientComponent | FieldServerComponent>;
            /**
             * The Filter component has to be a client component
             */
            Filter?: PayloadComponent;
        };
    

    So from the Admin definition you can see that the Field property should be of type

    Field?: PayloadComponent<FieldClientComponent | FieldServerComponent>;