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,
};
},
],
},
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.