Using blocks in forms
This guide describes how to use a block in a Final Form.
We use a product form with a DamImageBlock
as an example.
The product has the following schema in GraphQL:
type Product {
id: ID!
title: String!
image: DamImageBlockData!
}
The following steps are necessary to add the image
field to the form:
- Create a Final Form component for the block
- Create the block state when initializing the form
- Create the block output when submitting the form
The block transitions between the various states when being used in a form. The states and transitions can be viewed here.
Create a Final Form component for the block
The block's AdminComponent
API (state
, updateState
) must be converted to the Field
API (input.value
, input.onChange
).
Use the createFinalFormBlock helper for this:
import { createFinalFormBlock } from "@comet/blocks-admin";
const FinalFormDamImageBlock = createFinalFormBlock(DamImageBlock);
Use the newly created component in the corresponding Field
via the component
prop:
import isEqual from "lodash.isequal";
<Field
name="image"
isEqual={isEqual}
component={FinalFormDamImageBlock}
/>;
A deep equality check using isEqual
is necessary because the block state is an object.
Create the block state when initializing the form
The block state needs to be created when initializing the form. If a product exists (i.e., in edit mode), transform the block input to the block state. When creating a new product, use the block's default values:
import { BlockState } from "@comet/blocks-admin";
import isEqual from "lodash.isequal";
type FormValues = Omit<GQLProductFormFragment, "image"> & {
image: BlockState<typeof DamImageBlock>;
};
const { data } = useQuery(productFormQuery, id ? { variables: { id } } : { skip: true });
const initialValues = useMemo<Partial<FormValues>>(
() =>
data?.product
? {
...filterByFragment<GQLProductFormFragment>(productFormFragment, data.product),
image: DamImageBlock.input2State(data.product.image),
}
: {
inStock: false,
image: DamImageBlock.defaultValues(),
},
[data],
);
<FinalForm
initialValues={initialValues}
initialValuesEqual={isEqual}
/>;
A deep equality check using initialValuesEqual
is necessary when using blocks in a form.
Create the block output when submitting the form
Transform the block state to the block output before submitting the form:
const handleSubmit = async (values: FormValues) => {
const input = {
...values,
image: DamImageBlock.state2Output(values.image),
};
/* Create or update the product... */
};