Using Payload Form Field Components in your custom components

default discord avatar
ninahorne
last year
1 4

I'm trying to build out some custom form components, but I want them to be styled like the default payload components. I am able to import the components, but they don't come with an "onChange" handler, meaning I can't get the data out of them... has anyone successfully done this? I didn't see anything in the docs about it.

  • discord user avatar
    jacobsfletch
    Payload Team
    last year

    Okay @ninahorne version 0.13.6 deploys these new features into the Text, Textarea, Upload, and Select components.

    Your original request was for Select, so here's an example what a custom select component might look like (and here's the config for that field).

    It works exactly as you might expect by exposing the onChange and value for you to manage freely. If you want this value to continue saving to the document (as the example illustrates), you can still sync it with context via the useField hook.

    Let us know if you have any troubles wiring this in!

    3 replies
  • default discord avatar
    ninahorne
    last year

    Thank you so much!! I will let you know if I have an issues getting this up and running.

  • default discord avatar
    ninahorne
    last year

    Hi @jacobsfletch I am in version 0.13.6 but I am unable to reference the SelectInput or SelectInputType in the example. The Select field also appears to be the same as before, with no onChange handler. Am I missing something? Here's my package.json:

    
    {
      "name": "payload-starter-typescript",
      "description": "Blank template - no collections",
      "version": "1.0.0",
      "main": "dist/server.js",
      "license": "MIT",
      "scripts": {
        "dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
        "build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
        "build:server": "tsc",
        "build": "yarn build:payload && yarn build:server",
        "serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js"
      },
      "dependencies": {
        "axios": "^0.23.0",
        "coa": "^3.0.1",
        "dotenv": "^8.2.0",
        "express": "^4.17.1",
        "payload": "^0.13.6",
        "styled-components": "^5.3.1",
        "svgo": "^2.8.0",
        "twilio": "^3.71.1",
        "webpack": "^5.58.2"
      },
      "devDependencies": {
        "@types/express": "^4.17.9",
        "@types/styled-components": "^5.1.15",
        "cross-env": "^7.0.3",
        "nodemon": "^2.0.14",
        "ts-node": "^9.1.1",
        "typescript": "^4.1.3"
      }
    }
    
    
  • discord user avatar
    jmikrut
    Payload Team
    last year

    Hey @ninahorne — we just published a beta version where you can now import solely the inputs of the built-in Text and Select components. We'll be exposing more over time but for now these two should get you started!

    If you yarn add payload@0.13.8-beta.0 - you'll then be able to import the inputs themselves like this:

    import { TextInput, SelectInput } from 'payload/components/forms';

    This should be what you need!

    We'll document this information fully as soon as we publish a patch version inclusive of these changes. šŸ‘

  • discord user avatar
    DanRibbens
    Payload Team
    last year

    Have you seen the blog post on custom field components? https://payloadcms.com/blog/building-a-custom-field This goes a lot more in depth than what we put in the docs.

    If you are importing fields directly from Payload, you might not have access to everything you need. Instead you can import the styles and use the setValue from useFieldType.

    Does this help?

    4 replies
    default discord avatar
    ninahorne
    last year

    Yea, I've read the article, but I'm still struggling to replicate the Select field. Do you have any recommendations for this specific field type?

    discord user avatar
    DanRibbens
    Payload Team
    last year

    Can I ask what you custom functionality are you making with your select component? If it is something generally useful, it could be a candidate for a feature request.

    You could recreate it by copying from the original and change what you need. This might not be the best way though.

    Here is the component: https://github.com/payloadcms/payload/blob/master/src/admin/components/forms/field-types/Select/index.tsx

    @jmikrut might have better ideas.

    default discord avatar
    ninahorne
    last year

    Basically, I have a component that has a list of careers and when a user selects a career it calls an API to get data for that career to populate a few more fields.

    At first, I thought I could do this with a hook that waited for the value of the select to change, but I need this to happen instantaneously, not after the form is saved. So I guess my extension of the Select field is to add a custom onChange event to trigger an API call and then actually use the value from that call to update the form field.

    Maybe my approach is incorrect. Is there a better way to trigger events when a field updates?

    discord user avatar
    DanRibbens
    Payload Team
    last year

    I've done some experimenting on this and I may have found an actual bug, or that I'm using the useFieldType in a way that it wasn't intended.

    I thought it would be possible to have an additional useFieldType with a path to another field within the same form to access setValue of additional fields.

      const { setValue: setListNameValue } = useFieldType({
        path: 'listName',
      });
    
      useEffect(() => {
        setListNameValue(`${value} List`, true);
      }, [value]);

    In my code, setListNameValue is being called, but the value isn't being set properly. Debugging seems to indicate a race condition whereby the value change is dispatched as intended, but then another dispatch hits and immediately sets the listName value back.

    I like what you're trying to achieve with the component, we should be able to make this work. I've pushed the concept I was playing with here: https://github.com/payloadcms/custom-field-guide/pull/new/adjacent-field-setvalue

    I'll wait and see if others have any insight on this one.

  • discord user avatar
    jmikrut
    Payload Team
    last year

    Hey @ninahorne — I see what you're looking to do. I have been thinking about this myself actually in another project.

    We could extend built-in field components like Select and Text so that they accept an onChange and value handler.

    If either onChange or value was passed to a field component, we would disable its own internal useFieldType hook, and it would be up to you to send form values to and from the Payload form manually.

    Then you could benefit from the exact same components that Payload UI uses.

    Alternatively, right now, Payload SCSS is already exported and able to be re-used in custom components. There is a formInput mixin that styles inputs easily but you'd still have to recreate your form's label and surrounding structure.

    This approach would be easy enough and probably more performant for simple components like text inputs, but for Select fields it could get more complicated due to having to replicate Payload's internal use of react-select.

    For Select fields, another option there would be that Payload could export its internal ReactSelect implementation which does take an onChange and value prop already. That component is shared between the relationship and select field types as well as a few other places in the UI.

    Is there a route that you would prefer? We could work something up pretty quickly I'd expect!

    7 replies
    default discord avatar
    ninahorne
    last year

    I love the idea of adding an onChange or value handler! I could see many uses for that in my project, and I think it would be easier to keep my codebase more organized this way.

    That's so awesome! Thank you so much

    discord user avatar
    jacobsfletch
    Payload Team
    last year

    @ninahorne in the meantime could subscribe to your field's value using the useWatchForm hook. An added benefit here is that form state is debounced. You would just need to be sure that your component doesn't re-render on every field change of the entire document, since this would happen very often.

    default discord avatar
    ninahorne
    last year

    @jacobsfletch that's really interesting! can you show me the docs for that?

    discord user avatar
    jacobsfletch
    Payload Team
    last year

    @ninahorne there are no docs for this (yet) but here's a little snippet to help get you started:

    import { useWatchForm } from 'payload/components/forms';
    import { Props as TextFieldType } from 'payload/dist/admin/components/forms/field-types/Text/types';
    
    export const CustomComponent: React.FC<TextFieldType> = (props) => {
      const { path } = props;
    
      const { fields } = useWatchForm();
    
      const { value } = fields[path];
    
      return (
        <div>
          {value}
        </div>
      )
    }
    

    I hope this is helpful!

    discord user avatar
    jacobsfletch
    Payload Team
    last year

    @ninahorne we are actively working through the solution that @jmikrut described. You can follow the action in this pull request.

    default discord avatar
    ninahorne
    last year

    Wow this looks great! looks like this is merged in? Can i update and check it out?

    discord user avatar
    jacobsfletch
    Payload Team
    last year

    We're almost there. One more merge (#376) should do it. I'll post the version number and demo code once it's been deployed.

  • default discord avatar
    ninahorne
    last year
Open the post
Continue the discussion in GitHub
Like what we're doing?
Star us on GitHub!

Star

Connect with the Payload Community on Discord

Discord

online

Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.