How to reset select value in custom component

default discord avatar
imcorfitz
2 months ago

I have created a custom field/component that fetches items from an array field on the same entry and makes them available as options in a dropdown for a different array field. (You can consider it some sort of localised taxonomy.)



I fetch the data correctly, and make the options available correctly in the select field. However, when I remove an item from the source array, and set the value of the select component to null, it preserves the value of the now deleted item.



Please see images and code below.



I start by adding items to the chapters array. Then they are made available in the chapters select field. But when I remove an item that has been selected, the value remains, eventhough I try to clean up the value in the react component.



My chapters field:


 {
          label: "Chapters",
          fields: [
            {
              name: "chapters",
              label: "Chapters",
              type: "array",
              fields: [
                {
                  name: "title",
                  label: "Title",
                  type: "text",
                  localized: true,
                },
              ],
              admin: {
                components: {
                  RowLabel: ({ data }) => {
                    return data.title;
                  },
                },
              },
            },
          ],
        },


My custom chapterSelect field:


const chapterSelectField: Field = {
  name: "chapter",
  type: "text",
  admin: {
    components: {
      Field: InputField,
    },
  },
};


And the component:


import React from "react";

import { useFormFields, useField, Select } from "payload/components/forms";

// we can use existing Payload types easily
import { Props } from "payload/components/fields/Text";

const InputField: React.FC<Props> = (props) => {
  const { path, label, required, name } = props;

  const { value, setValue } = useField({ path });

  const chapters = useFormFields(([fields]) => {
    const numberOfChapters = parseInt(fields.chapters.value as string);

    const chapterFields = [];

    for (let i = 0; i < numberOfChapters; i++) {
      chapterFields.push({
        label: fields[`chapters.${i}.title`].value,
        value: fields[`chapters.${i}.id`].value,
      });
    }

    return JSON.stringify(chapterFields);
  });

  React.useEffect(() => {
    // Verify if the current value is in the list of chapters
    const chapter = JSON.parse(chapters).find((cph) => cph.value === value);

    // If not, set the value to null
    if (!chapter) {
      setValue(null, true);
    }
  }, [chapters, value, setValue]);

  return (
    <Select
      name={name}
      label={label}
      path={path}
      required={required}
      options={JSON.parse(chapters)}
    />
  );
};

export default InputField;


It is worth noting, that this appears only to be cosmetic, as switching between tabs in the entry does re-render the select field a second time and applies the null state. However, if I simply save the entry after deleting a selected array item, it will keep the value from before.



I realise after writing this, that maybe this cleanup should in fact take place as an

afterDelete

hook on the chapters field 🤔



Yup.. Added a

beforeValidate

hook to the collection with the following, and it worked:


hooks: {
    beforeValidate: [
      ({ data }) => {
        const entries = data.entries.map((entry) => {
          if (
            entry.chapter &&
            !data.chapters.find((chapter) => chapter.id === entry.chapter)
          ) {
            entry.chapter = null;
          }
          return entry;
        });

        return {
          ...data,
          entries,
        };
      },
    ],
  },
    Open the post
    Continue the discussion in Discord
    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.