-
I'm trying to use both the react form builder and the factory pattern to have a textEntity and emailEntity with validation with access to a db. Everything seems to be working fine except when I select either of the entities "use client";
import { BuilderEntities, useBuilderStore } from "@coltorapps/builder-react";
import { useEffect, useState } from "react";
import { Button } from "./core/button";
import { LabelAttribute } from "./labelAttribute";
import { RequiredAttribute } from "./requiredAttribute";
import { TextFieldEntity } from "./textFieldEntity";
import { saveFormSchema } from "../actions";
import { createFormBuilder } from "../definitions/formBuilder";
import { EmailFieldEntity } from "./emailFieldEntity";
import { BuilderEntityAttributes } from "./builderEntityAttributes";
/*
| We define a `TextFieldAttributes` component,
| which is responsible for rendering the attributes
| of a text field (currently, it only includes the
| label attribute).
*/
function TextFieldAttributes() {
return (
<div>
<LabelAttribute />
<RequiredAttribute />
</div>
);
}
function EmailFieldAttributes() {
return (
<div>
<LabelAttribute />
</div>
);
}
export default function FormBuilderPage() {
/*
| We declare an `activeEntityId` state variable,
| which holds an optional reference to the currently
| active entity ID.
*/
const { formBuilder } = createFormBuilder();
const [activeEntityId, setActiveEntityId] = useState<string | undefined>();
/*
| We utilize the `useBuilderStore` hook, which creates
| a builder store for us. This store is responsible for
| building a schema based on a builder definition.
*/
const builderStore = useBuilderStore(formBuilder, {
events: {
/*
| We use the `onEntityAttributeUpdated` event callback
| to trigger an arbitrary attribute validation every time
| its value is updated.
*/
onEntityAttributeUpdated(payload) {
void builderStore.validateEntityAttribute(
payload.entity.id,
payload.attributeName
);
},
/*
| We use the `onEntityDeleted` event callback to unset the
| `activeEntityId` state variable when the currently active
| entity is deleted.
*/
onEntityDeleted(payload) {
if (payload.entity.id === activeEntityId) {
setActiveEntityId(undefined);
}
},
},
});
console.log("builderStore", builderStore);
async function submitFormSchema() {
/*
| We validate the schema once again on the client
| to trigger all the validations and provide the user
| with feedback on what needs to be corrected.
*/
const validationResult = await builderStore.validateSchema();
if (validationResult.success) {
// The schema is valid and can be sent to the server.
await saveFormSchema(validationResult.data);
}
}
return (
<div className="p-4 space-y-4">
{/*
| We use the `BuilderEntities` component to render the entities
| tree of the schema of our builder store.
| We pass the entity components for each defined entity type
| in our form builder (currently, it's only the text field).
*/}
<BuilderEntities
builderStore={builderStore}
components={{
textField: TextFieldEntity,
emailField: EmailFieldEntity,
}}
>
{/*
| We leverage the render prop of the `BuilderEntities` component
| to wrap each rendered arbitrary entity with additional
| rendering.
*/}
{(props) => (
<div className="flex space-x-4">
{/* This represents each rendered arbitrary entity. */}
{props.children}
{/*
| A button that marks the arbitrary entity as active,
| allowing the user to edit its attributes.
*/}
<Button
variant={"outline"}
type="button"
onClick={() => {
setActiveEntityId(props.entity.id);
}}
>
Select
</Button>
{/*
| A delete button is rendered next to each entity,
| that removes the entity from the store's schema.
*/}
<Button
variant={"destructive"}
type="button"
onClick={() => {
builderStore.deleteEntity(props.entity.id);
}}
>
Delete
</Button>
</div>
)}
</BuilderEntities>
{/*
| A button that adds a new text field type entity
| to the store's schema.
*/}
<Button
variant={"outline"}
type="button"
onClick={() =>
builderStore.addEntity({
type: "textField",
attributes: { label: "Text Field" },
})
}
>
Add Text Field
</Button>
<Button
variant={"outline"}
type="button"
onClick={() => {
builderStore.addEntity({
type: "emailField",
attributes: { label: "Email Field" },
});
console.log("builderStore get data", builderStore.getData());
}}
>
Add Email Field
</Button>
{/*
| We render the `BuilderEntityAttributes` component only when
| an entity is active. We also provide the components
| that render attribute components for each defined
| entity type in the builder (currently, it's only the
| text field).
*/}
{activeEntityId ? (
<BuilderEntityAttributes
builderStore={builderStore}
entityId={activeEntityId}
components={{
textField: TextFieldAttributes,
emailField: EmailFieldAttributes,
}}
/>
) : null}
<Button
variant={"outline"}
type="button"
onClick={() => void submitFormSchema()}
>
Save Form
</Button>
</div>
);
} I am getting the following error:
It looks like somehow the schema.entities are being reset to and empty object all the time, when I try to debug with builderStore.getData(). Any pointers on how to solve this? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
@vondersam Perhaps it's because the component re-renders when you call |
Beta Was this translation helpful? Give feedback.
@vondersam Perhaps it's because the component re-renders when you call
setActiveEntityId
, causing the builder to be recreated and, consequently, the store as well, since it depends on it. Try creating the builder outside the component or memoizing it.