-
|
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.